├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── deploy └── resources │ └── bin │ ├── ZooViewer.bat │ └── zooviewer.sh └── main ├── assembly └── bin.xml ├── java └── net │ └── isammoc │ └── zooviewer │ ├── App.java │ ├── model │ ├── ZVModel.java │ ├── ZVModelImpl.java │ └── ZVModelListener.java │ ├── node │ ├── JZVNode.java │ ├── JZVStat.java │ ├── ZVNode.java │ └── ZVNodeImpl.java │ └── tree │ ├── JZVTree.java │ └── ZVTreeModel.java └── resources └── net └── isammoc └── zooviewer ├── App.properties └── node └── JZVNode.properties /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZooViewer 2 | 3 | [![Project Status: Unsupported – The project has reached a stable, usable state but the author(s) have ceased all work on it. A new maintainer may be desired.](http://www.repostatus.org/badges/latest/unsupported.svg)](http://www.repostatus.org/#unsupported) 4 | 5 | 6 | ZooKeeper configuration viewer 7 | 8 | 9 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | net.isammoc.zooviewer 4 | zooviewer 5 | 0.6-SNAPSHOT 6 | 7 | ZooViewer 8 | A graphical application to view and manage ZooKeeper 9 | 10 | 2010 11 | http://code.google.com/p/zooviewer/ 12 | 13 | 14 | 15 | GNU Lesser General Public License v3+ 16 | http://www.gnu.org/licenses/lgpl.html 17 | repo 18 | 19 | 20 | 21 | 22 | http://code.google.com/p/zooviewer/source/browse/trunk 23 | scm:svn:http://zooviewer.googlecode.com/svn/trunk 24 | scm:svn:https://zooviewer.googlecode.com/svn/trunk 25 | 26 | 27 | 28 | Google Code Issue Tracking 29 | http://code.google.com/p/zooviewer/issues/list 30 | 31 | 32 | 33 | http://code.google.com/p/zooviewer/downloads/list 34 | 35 | false 36 | zooviewer.googlecode.releases 37 | Official ZooViewer repository 38 | scm:svn:https://zooviewer.googlecode.com/svn/maven2/ 39 | default 40 | 41 | 42 | false 43 | zooviewer.googlecode.snapshots 44 | ZooViewer repository for snapshot versions 45 | scm:svn:https://zooviewer.googlecode.com/svn/maven2-snapshots/ 46 | default 47 | 48 | 49 | zooviewer.googlecode.site 50 | ZooViewer site 51 | scm:svn:https://zooviewer.googlecode.com/svn/site 52 | 53 | 54 | 55 | 56 | 57 | Sébastien BREVET 58 | 59 | 60 | Franck LEFEVRE 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.apache.maven.wagon 68 | wagon-scm 69 | 1.0-beta-6 70 | 71 | 72 | org.apache.maven.scm 73 | maven-scm-manager-plexus 74 | 1.0 75 | 76 | 77 | org.apache.maven.scm 78 | maven-scm-provider-svnexe 79 | 1.0 80 | 81 | 82 | 83 | 84 | maven-compiler-plugin 85 | 86 | 1.6 87 | 1.6 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-jar-plugin 93 | 94 | 95 | 96 | true 97 | ../lib/ 98 | net.isammoc.zooviewer.App 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-assembly-plugin 106 | 2.2-beta-5 107 | 108 | 109 | src/main/assembly/bin.xml 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.apache.hadoop 118 | zookeeper 119 | 3.3.1 120 | 121 | 122 | jmxri 123 | com.sun.jmx 124 | 125 | 126 | jmxtools 127 | com.sun.jdmk 128 | 129 | 130 | 131 | 132 | junit 133 | junit 134 | 4.8.1 135 | jar 136 | test 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/deploy/resources/bin/ZooViewer.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | 5 | set TITLE=ZooViewer - %ZOO_HOST% 6 | title %TITLE% 7 | 8 | java -cp "..\lib\*" net.isammoc.zooviewer.App %* 9 | 10 | endlocal 11 | -------------------------------------------------------------------------------- /src/deploy/resources/bin/zooviewer.sh: -------------------------------------------------------------------------------- 1 | 2 | ZOO_HOST="$1" 3 | if [ -z "$ZOO_HOST%" ]; then 4 | ZOO_HOST="127.0.0.1:2181" 5 | fi 6 | 7 | java -cp "../lib/*" $JVMFLAGS net.isammoc.zooviewer.App "$ZOO_HOST" 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/assembly/bin.xml: -------------------------------------------------------------------------------- 1 | 2 | bin 3 | 4 | 5 | tar.gz 6 | tar.bz2 7 | zip 8 | dir 9 | 10 | 11 | 12 | 13 | src/deploy/resources 14 | 15 | 16 | 17 | 18 | 19 | 20 | true 21 | lib 22 | 23 | 24 | false 25 | bin 26 | 27 | ${project.artifactId}* 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/App.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer; 16 | 17 | import java.awt.Component; 18 | import java.awt.Frame; 19 | import java.awt.Window; 20 | import java.awt.event.WindowAdapter; 21 | import java.awt.event.WindowEvent; 22 | import java.io.IOException; 23 | import java.util.ResourceBundle; 24 | 25 | import javax.swing.JDialog; 26 | import javax.swing.JFrame; 27 | import javax.swing.JLabel; 28 | import javax.swing.JOptionPane; 29 | import javax.swing.JScrollPane; 30 | import javax.swing.JSplitPane; 31 | import javax.swing.JTree; 32 | import javax.swing.SwingUtilities; 33 | import javax.swing.UIManager; 34 | import javax.swing.UnsupportedLookAndFeelException; 35 | import javax.swing.event.TreeModelEvent; 36 | import javax.swing.event.TreeSelectionEvent; 37 | import javax.swing.event.TreeSelectionListener; 38 | import javax.swing.tree.DefaultTreeCellRenderer; 39 | import javax.swing.tree.TreePath; 40 | import javax.swing.tree.TreeSelectionModel; 41 | 42 | import net.isammoc.zooviewer.model.ZVModel; 43 | import net.isammoc.zooviewer.model.ZVModelImpl; 44 | import net.isammoc.zooviewer.node.JZVNode; 45 | import net.isammoc.zooviewer.node.ZVNode; 46 | import net.isammoc.zooviewer.tree.JZVTree; 47 | 48 | import org.apache.log4j.lf5.viewer.categoryexplorer.TreeModelAdapter; 49 | 50 | public class App { 51 | private static final String DEFAULT_CONNECTION_STRING = "127.0.0.1:2181"; 52 | private static ResourceBundle bundle = ResourceBundle.getBundle(App.class 53 | .getCanonicalName()); 54 | 55 | /** 56 | * @param args 57 | * @throws IOException 58 | */ 59 | public static void main(String[] args) throws IOException { 60 | String connexionString = null; 61 | if (args.length > 0) { 62 | connexionString = args[0]; 63 | } else { 64 | connexionString = inputConnectionString(DEFAULT_CONNECTION_STRING); 65 | if (connexionString == null) { 66 | System.err.println(bundle 67 | .getString("start.connection.aborted.message")); 68 | System.exit(2); 69 | } 70 | } 71 | 72 | try { 73 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 74 | } catch (ClassNotFoundException e1) { 75 | e1.printStackTrace(); 76 | } catch (InstantiationException e1) { 77 | e1.printStackTrace(); 78 | } catch (IllegalAccessException e1) { 79 | e1.printStackTrace(); 80 | } catch (UnsupportedLookAndFeelException e1) { 81 | e1.printStackTrace(); 82 | } 83 | 84 | // 85 | final ZVModel model = new ZVModelImpl(connexionString); 86 | final JZVNode nodeView = new JZVNode(model); 87 | final JZVTree tree = new JZVTree(model); 88 | 89 | String editorViewtitle = String.format("%s - Editor View - ZooViewer", 90 | connexionString); 91 | String displayViewtitle = String.format( 92 | "%s - Display View - ZooViewer", connexionString); 93 | 94 | final JFrame jfEditor = new JFrame(editorViewtitle); 95 | jfEditor.setName("zv_editor"); 96 | JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 97 | new JScrollPane(tree), nodeView); 98 | split.setDividerLocation(0.4); 99 | jfEditor.getContentPane().add(split); 100 | jfEditor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 101 | jfEditor.setSize(600, 400); 102 | jfEditor.setLocationRelativeTo(null); 103 | 104 | final JZVTree tree2 = createViewOnlyTree(model); 105 | 106 | final JFrame jfDisplay = new JFrame(displayViewtitle); 107 | jfDisplay.setName("zv_display"); 108 | jfDisplay.getContentPane().add(new JScrollPane(tree2)); 109 | jfDisplay.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 110 | jfDisplay.setSize(300, 400); 111 | jfDisplay.setLocation(jfEditor.getLocation().x - jfDisplay.getWidth(), 112 | jfEditor.getLocation().y); 113 | jfDisplay.setVisible(true); 114 | 115 | tree.addTreeSelectionListener(new TreeSelectionListener() { 116 | @Override 117 | public void valueChanged(TreeSelectionEvent e) { 118 | // Create the array of selections 119 | TreePath[] selPaths = tree.getSelectionModel() 120 | .getSelectionPaths(); 121 | if (selPaths == null) { 122 | return; 123 | } 124 | ZVNode[] nodes = new ZVNode[selPaths.length]; 125 | for (int i = 0; i < selPaths.length; i++) { 126 | nodes[i] = (ZVNode) selPaths[i].getLastPathComponent(); 127 | } 128 | nodeView.setNodes(nodes); 129 | } 130 | }); 131 | 132 | jfEditor.addWindowListener(new WindowAdapter() { 133 | @Override 134 | public void windowClosing(WindowEvent e) { 135 | jfEditor.dispose(); 136 | if (!jfDisplay.isDisplayable()) { 137 | try { 138 | model.close(); 139 | } catch (InterruptedException e1) { 140 | // TODO Auto-generated catch block 141 | e1.printStackTrace(); 142 | } 143 | disposeOtherWindows(jfEditor, jfDisplay); 144 | } 145 | } 146 | }); 147 | 148 | jfDisplay.addWindowListener(new WindowAdapter() { 149 | @Override 150 | public void windowClosing(WindowEvent e) { 151 | jfDisplay.dispose(); 152 | if (!jfEditor.isDisplayable()) { 153 | try { 154 | model.close(); 155 | } catch (InterruptedException e1) { 156 | // TODO Auto-generated catch block 157 | e1.printStackTrace(); 158 | } 159 | disposeOtherWindows(jfEditor, jfDisplay); 160 | } 161 | } 162 | }); 163 | 164 | DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() { 165 | /** */ 166 | private static final long serialVersionUID = 1L; 167 | 168 | @Override 169 | public Component getTreeCellRendererComponent(JTree tree, 170 | Object value, boolean sel, boolean expanded, boolean leaf, 171 | int row, boolean hasFocus) { 172 | Component comp = super.getTreeCellRendererComponent(tree, 173 | value, sel, expanded, leaf, row, hasFocus); 174 | if ((comp instanceof JLabel) && (value instanceof ZVNode)) { 175 | ZVNode node = (ZVNode) value; 176 | String text = node.getName(); 177 | byte[] data = node.getData(); 178 | if ((data != null) && (data.length > 0)) { 179 | text += "=" + new String(data); 180 | } 181 | ((JLabel) comp).setText(text); 182 | ((JLabel) comp).validate(); 183 | } 184 | return comp; 185 | } 186 | }; 187 | tree2.setCellRenderer(renderer); 188 | tree.setCellRenderer(renderer); 189 | 190 | jfEditor.setVisible(true); 191 | } 192 | 193 | private static String inputConnectionString(String defaultString) { 194 | JOptionPane pane = new JOptionPane( 195 | bundle.getString("start.connection.message"), 196 | JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION); 197 | pane.setWantsInput(true); 198 | pane.setInputValue(defaultString); 199 | pane.setInitialValue(defaultString); 200 | pane.setInitialSelectionValue(defaultString); 201 | 202 | JDialog dialog = pane.createDialog(null, 203 | bundle.getString("start.connection.title")); 204 | dialog.setVisible(true); 205 | 206 | String connectionString = null; 207 | Object inputValue = pane.getInputValue(); 208 | if (inputValue == null) { 209 | return null; 210 | } 211 | connectionString = (String) inputValue; 212 | if (connectionString.equals("")) { 213 | connectionString = DEFAULT_CONNECTION_STRING; 214 | } 215 | return inputValue == null ? null : (String) inputValue; 216 | } 217 | 218 | private static void disposeOtherWindows(final JFrame jf, final JFrame jf2) { 219 | Window[] windows = Frame.getOwnerlessWindows(); 220 | for (Window window : windows) { 221 | if (!window.getName().equals(jf) && !window.getName().equals(jf2)) { 222 | window.dispose(); 223 | } 224 | } 225 | } 226 | 227 | private static JZVTree createViewOnlyTree(final ZVModel model) { 228 | final JZVTree tree2 = new JZVTree(model); 229 | tree2.setExpandsSelectedPaths(true); 230 | tree2.getSelectionModel().setSelectionMode( 231 | TreeSelectionModel.SINGLE_TREE_SELECTION); 232 | 233 | tree2.getModel().addTreeModelListener(new TreeModelAdapter() { 234 | 235 | @Override 236 | public void treeNodesChanged(TreeModelEvent e) { 237 | System.out 238 | .println("App.main(...).new TreeModelAdapter() {...}.treeNodesChanged()"); 239 | final TreePath childPath = e.getTreePath().pathByAddingChild( 240 | e.getChildren()[0]); 241 | this.selectAndDisplayPath(tree2, childPath); 242 | } 243 | 244 | @Override 245 | public void treeNodesInserted(TreeModelEvent e) { 246 | System.out 247 | .println("App.main(...).new TreeModelAdapter() {...}.treeNodesInserted()"); 248 | Object[] children = e.getChildren(); 249 | final TreePath lastChildPath = e.getTreePath() 250 | .pathByAddingChild(children[children.length - 1]); 251 | this.selectAndDisplayPath(tree2, lastChildPath); 252 | } 253 | 254 | @Override 255 | public void treeNodesRemoved(TreeModelEvent e) { 256 | System.out 257 | .println("App.main(...).new TreeModelAdapter() {...}.treeNodesRemoved()"); 258 | this.selectAndDisplayPath(tree2, e.getTreePath()); 259 | } 260 | 261 | private void selectAndDisplayPath(final JZVTree tree2, 262 | final TreePath childPath) { 263 | SwingUtilities.invokeLater(new Runnable() { 264 | @Override 265 | public void run() { 266 | TreeSelectionModel selectionModel = tree2 267 | .getSelectionModel(); 268 | selectionModel.clearSelection(); 269 | selectionModel.addSelectionPath(childPath); 270 | 271 | tree2.scrollPathToVisible(childPath); 272 | } 273 | }); 274 | } 275 | }); 276 | return tree2; 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/model/ZVModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.model; 16 | 17 | import java.util.List; 18 | 19 | import net.isammoc.zooviewer.node.ZVNode; 20 | 21 | /** 22 | * Interface to the ZooViewer model. 23 | * 24 | * @author franck 25 | */ 26 | public interface ZVModel { 27 | /** 28 | * Adds a {@link ZVModelListener} to the listener list. 29 | * 30 | * @param listener the {@link ZVModelListener} to be added 31 | */ 32 | void addModelListener(ZVModelListener listener); 33 | 34 | /** 35 | * Removes a {@link ZVModelListener} from the listener list. 36 | * 37 | * @param listener the {@link ZVModelListener} to be added 38 | */ 39 | void removeModelListener(ZVModelListener listener); 40 | 41 | /** 42 | * Adds a node to the ZooKeeper model. 43 | * @param path the node path 44 | * @param data the node data 45 | */ 46 | void addNode(String path, byte[] data); 47 | 48 | /** 49 | * Updates a node's data in the ZooKeeper model. 50 | * @param path the node path 51 | * @param data the node data 52 | */ 53 | void updateData(String path, byte[] data); 54 | 55 | /** 56 | * Deletes a node and his children. 57 | * @param node the node to be deleted 58 | */ 59 | void deleteNode(ZVNode node); 60 | 61 | /** 62 | * Deletes a list of nodes and their children. 63 | * @param paths the nodes to be deleted 64 | */ 65 | void deleteNodes(ZVNode[] nodes); 66 | 67 | /** 68 | * Returns a {@link ZVNode} corresponding to the specified path. 69 | * @param path the node path 70 | * @return the {@link ZVNode} instance, or null if the node doesn't exist 71 | */ 72 | ZVNode getNode(String path); 73 | 74 | /** 75 | * Returns the parent of the specified {@link ZVNode}. 76 | * @param node the node 77 | * @return the parent {@link ZVNode} 78 | */ 79 | ZVNode getParent(ZVNode node); 80 | 81 | /** 82 | * Returns the list of child nodes under the specified parent. 83 | * @param parent the parent node 84 | * @return the list of child nodes, or an empty list if parent has no children 85 | */ 86 | List getChildren(ZVNode parent); 87 | 88 | /** 89 | * Returns a full path from a parent node and name of child. 90 | * 91 | * @param parentPath 92 | * Parent path 93 | * @param childName 94 | * Child name 95 | * @return full path 96 | */ 97 | String getFullPath(String parentPath, String childName); 98 | 99 | /** 100 | * Closes the ZooKeeper connection. 101 | * @throws InterruptedException 102 | */ 103 | void close() throws InterruptedException; 104 | } -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/model/ZVModelImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.model; 16 | 17 | import java.io.IOException; 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.Iterator; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | 26 | import javax.swing.event.EventListenerList; 27 | 28 | import net.isammoc.zooviewer.node.ZVNode; 29 | import net.isammoc.zooviewer.node.ZVNodeImpl; 30 | 31 | import org.apache.zookeeper.AsyncCallback.ChildrenCallback; 32 | import org.apache.zookeeper.CreateMode; 33 | import org.apache.zookeeper.KeeperException; 34 | import org.apache.zookeeper.WatchedEvent; 35 | import org.apache.zookeeper.Watcher; 36 | import org.apache.zookeeper.ZooKeeper; 37 | import org.apache.zookeeper.common.PathUtils; 38 | import org.apache.zookeeper.data.Stat; 39 | 40 | /** 41 | * Implementation of the ZooViewer model. 42 | * 43 | * @author franck 44 | */ 45 | public class ZVModelImpl implements ZVModel { 46 | 47 | protected final EventListenerList listenerList = new EventListenerList(); 48 | private final ZooKeeper zk; 49 | private final ExecutorService watcherExecutor = Executors 50 | .newSingleThreadExecutor(); 51 | private final Map nodes = new HashMap(); 52 | private final Map> children = new HashMap>(); 53 | private final ZkWatcher watcher; 54 | 55 | private final class ZkWatcher implements Watcher { 56 | private final Object lock = new Object(); 57 | private volatile boolean dead = true; 58 | 59 | @Override 60 | public void process(WatchedEvent event) { 61 | System.out.println("[" + Thread.currentThread() + "event : " 62 | + event); 63 | switch (event.getType()) { 64 | case None: 65 | switch (event.getState()) { 66 | case Disconnected: 67 | case Expired: 68 | System.out.println("[" + Thread.currentThread() 69 | + "Session has expired"); 70 | synchronized (lock) { 71 | dead = true; 72 | lock.notifyAll(); 73 | } 74 | break; 75 | case SyncConnected: 76 | System.out.println("[" + Thread.currentThread() 77 | + "Connected to the server"); 78 | synchronized (lock) { 79 | dead = false; 80 | lock.notifyAll(); 81 | // populateView(); 82 | } 83 | break; 84 | } 85 | zk.register(this); 86 | break; 87 | case NodeCreated: 88 | System.out.println("Node " + event.getPath() + " created"); 89 | // FLE+ 90 | // nodeDataChanged(event.getPath()); 91 | break; 92 | case NodeChildrenChanged: 93 | System.out.println("Children changed for node " 94 | + event.getPath()); 95 | populateChildren(event.getPath()); 96 | break; 97 | case NodeDeleted: 98 | System.out.println("Node " + event.getPath() + " deleted"); 99 | nodeDeleted(event.getPath()); 100 | break; 101 | case NodeDataChanged: 102 | System.out.println("Data changed for node " 103 | + event.getPath()); 104 | nodeDataChanged(event.getPath()); 105 | break; 106 | } 107 | } 108 | } 109 | 110 | public ZVModelImpl(String connectString) throws IOException { 111 | this.watcher = new ZkWatcher(); 112 | this.zk = new ZooKeeper(connectString, 3000, this.watcher); 113 | // s this.watcherExecutor.execute(this.watcher); 114 | 115 | System.out.println("[" + Thread.currentThread() + "] AFTER ZK INIT"); 116 | synchronized (watcher.lock) { 117 | while (watcher.dead) { 118 | try { 119 | System.out.println("[" + Thread.currentThread() 120 | + "Awaiting lock notification"); 121 | watcher.lock.wait(); 122 | System.out.println("[" + Thread.currentThread() 123 | + "Lock notification, watcher.dead = " 124 | + watcher.dead); 125 | } catch (InterruptedException e) { 126 | // TODO Auto-generated catch block 127 | e.printStackTrace(); 128 | } 129 | } 130 | } 131 | populateRoot(); 132 | 133 | } 134 | 135 | /* 136 | * (non-Javadoc) 137 | * 138 | * @see net.isammoc.zooviewer.model.ZVModel#close() 139 | */ 140 | @Override 141 | public void close() throws InterruptedException { 142 | System.out.println("Closing ZooKeeper client..."); 143 | zk.close(); 144 | synchronized (watcher.lock) { 145 | watcher.dead = true; 146 | watcher.lock.notifyAll(); 147 | } 148 | System.out.println("Shutting down watcher..."); 149 | watcherExecutor.shutdown(); 150 | System.out.println("Removing listeners..."); 151 | ZVModelListener[] listeners = listenerList 152 | .getListeners(ZVModelListener.class); 153 | for (int i = 0; i < listeners.length; i++) { 154 | listenerList.remove(ZVModelListener.class, listeners[i]); 155 | } 156 | 157 | System.out.println("Resetting models..."); 158 | nodes.clear(); 159 | children.clear(); 160 | System.out.println("Close done."); 161 | } 162 | 163 | /** 164 | * Called when a node has been deleted in the ZooKeeper model. 165 | * @param path the node path 166 | */ 167 | private synchronized void nodeDeleted(String path) { 168 | ZVNodeImpl oldNode = nodes.get(path); 169 | if (oldNode != null) { 170 | oldNode.setExists(false); 171 | oldNode.setStat(null); 172 | ZVNodeImpl parent = nodes.get(getParent(path)); 173 | int oldIndex = children.get(parent).indexOf(oldNode); 174 | children.get(parent).remove(oldNode); 175 | fireNodeDeleted(oldNode, oldIndex); 176 | } 177 | } 178 | 179 | /** 180 | * Called when a node has been updated in the ZooKeeper model. 181 | * @param path the node path 182 | */ 183 | private synchronized void nodeDataChanged(String path) { 184 | ZVNodeImpl node = nodes.get(path); 185 | try { 186 | Stat stat = new Stat(); 187 | node.setData(zk.getData(path, watcher, stat)); 188 | node.setStat(stat); 189 | fireNodeDataChanged(node); 190 | } catch (KeeperException e) { 191 | e.printStackTrace(); 192 | } catch (InterruptedException e) { 193 | e.printStackTrace(); 194 | } 195 | } 196 | 197 | /** 198 | * Populates the root in this model. 199 | * @param path the node path 200 | */ 201 | private synchronized void populateRoot() { 202 | if (nodes.get("/") == null) { 203 | try { 204 | System.out.println("[" + Thread.currentThread() 205 | + "Populating root.."); 206 | Stat stat = new Stat(); 207 | ZVNodeImpl root = new ZVNodeImpl("/", zk.getData("/", watcher, 208 | stat)); 209 | root.setStat(stat); 210 | nodes.put("/", root); 211 | children.put(root, new ArrayList()); 212 | fireNodeCreated(root); 213 | populateChildren("/"); 214 | } catch (KeeperException e) { 215 | e.printStackTrace(); 216 | } catch (InterruptedException e) { 217 | e.printStackTrace(); 218 | } 219 | } 220 | } 221 | 222 | /** 223 | * Populates the children of the specified path. 224 | * @param path 225 | */ 226 | private synchronized void populateChildren(String path) { 227 | ChildrenCallback cb = new ChildrenCallback() { 228 | 229 | @Override 230 | public void processResult(int rc, String path, Object ctx, 231 | List childrenNames) { 232 | ZVNodeImpl parent = nodes.get(path); 233 | Stat stat = new Stat(); 234 | try { 235 | parent.setStat(zk.exists(path, false)); 236 | } catch (Exception ignore) { 237 | ignore.printStackTrace(); 238 | } 239 | for (String childName : childrenNames) { 240 | try { 241 | String childPath = getFullPath(path, childName); 242 | ZVNodeImpl child = nodes.get(childPath); 243 | if (child != null) { 244 | if (!child.exists()) { 245 | child.setData(zk.getData(childPath, watcher, 246 | stat)); 247 | child.setStat(stat); 248 | child.setExists(true); 249 | children.put(child, new ArrayList()); 250 | children.get(parent).add(child); 251 | fireNodeCreated(child); 252 | populateChildren(childPath); 253 | } 254 | } else { 255 | child = new ZVNodeImpl(childPath, zk.getData( 256 | childPath, watcher, stat)); 257 | child.setStat(stat); 258 | nodes.put(childPath, child); 259 | children.put(child, new ArrayList()); 260 | children.get(parent).add(child); 261 | fireNodeCreated(child); 262 | populateChildren(childPath); 263 | } 264 | } catch (Exception ignore) { 265 | ignore.printStackTrace(); 266 | } 267 | } 268 | } 269 | }; 270 | zk.getChildren(path, watcher, cb, null); 271 | } 272 | 273 | /* 274 | * (non-Javadoc) 275 | * 276 | * @see net.isammoc.zooviewer.model.ZVModel#getFullPath(java.lang.String, 277 | * java.lang.String) 278 | */ 279 | @Override 280 | public String getFullPath(String parentPath, String childName) { 281 | return ("/".equals(parentPath) ? "/" : (parentPath + "/")) + childName; 282 | } 283 | 284 | /** 285 | * @param path 286 | * @return 287 | */ 288 | private String getParent(String path) { 289 | if ("/".equals(path)) { 290 | return null; 291 | } else { 292 | int lastIndex = path.lastIndexOf("/"); 293 | if (lastIndex > 0) { 294 | return path.substring(0, lastIndex); 295 | } else { 296 | return "/"; 297 | } 298 | } 299 | } 300 | 301 | @Override 302 | public void addNode(String path, byte[] data) { 303 | if ((nodes.get(path) != null) && nodes.get(path).exists()) { 304 | throw new IllegalStateException("Node '" + path 305 | + "' already exists"); 306 | } 307 | 308 | if ((nodes.get(getParent(path)) == null) 309 | || !nodes.get(getParent(path)).exists()) { 310 | throw new IllegalArgumentException("Node '" + path 311 | + "' can't be created. Its parent node doesn't exist"); 312 | } 313 | 314 | try { 315 | zk.create(path, data, 316 | org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE, 317 | CreateMode.PERSISTENT); 318 | } catch (KeeperException e) { 319 | e.printStackTrace(); 320 | } catch (InterruptedException e) { 321 | e.printStackTrace(); 322 | } 323 | } 324 | 325 | @Override 326 | public void deleteNode(ZVNode node) { 327 | String path = node.getPath(); 328 | System.out.println("Delete requested on node " + path); 329 | PathUtils.validatePath(path); 330 | try { 331 | // Checks if the node has children 332 | List childNodes = zk.getChildren(path, false); 333 | if (childNodes != null && childNodes.size() > 0) { 334 | // if the node has children, delete them recursively 335 | for (Iterator iterator = childNodes.iterator(); iterator 336 | .hasNext();) { 337 | String nodeName = (String) iterator.next(); 338 | String childPath = path + (path.endsWith("/") ? "" : "/") 339 | + nodeName; 340 | deleteNode(getNode(childPath)); 341 | } 342 | } 343 | // finally, delete the node itself 344 | Stat stat = zk.exists(path, false); 345 | System.out.println("Deleting node " + path + "(stat = " + stat); 346 | zk.delete(path, -1); 347 | Stat stat2 = zk.exists(path, false); 348 | System.out.println("Deleting node " + path + "(stat = " + stat2); 349 | } catch (KeeperException e) { 350 | // TODO Auto-generated catch block 351 | e.printStackTrace(); 352 | } catch (InterruptedException e) { 353 | // TODO Auto-generated catch block 354 | e.printStackTrace(); 355 | } 356 | } 357 | 358 | @Override 359 | public void deleteNodes(ZVNode[] nodes) { 360 | for (int i = 0; i < nodes.length; i++) { 361 | deleteNode(nodes[i]); 362 | } 363 | 364 | } 365 | 366 | @Override 367 | public void updateData(String path, byte[] data) { 368 | try { 369 | Stat stat = zk.setData(path, data, -1); 370 | nodes.get(path).setStat(stat); 371 | } catch (KeeperException e) { 372 | e.printStackTrace(); 373 | } catch (InterruptedException e) { 374 | e.printStackTrace(); 375 | } 376 | } 377 | 378 | @Override 379 | public ZVNode getNode(String path) { 380 | return nodes.get(path); 381 | } 382 | 383 | @Override 384 | public ZVNode getParent(ZVNode node) { 385 | return getNode(getParent(node.getPath())); 386 | } 387 | 388 | @Override 389 | public List getChildren(ZVNode parent) { 390 | List nodes = new ArrayList(); 391 | for (ZVNode node : children.get(parent)) { 392 | if (node.exists()) { 393 | nodes.add(node); 394 | } 395 | } 396 | return nodes; 397 | } 398 | 399 | @Override 400 | public void addModelListener(ZVModelListener listener) { 401 | listenerList.add(ZVModelListener.class, listener); 402 | } 403 | 404 | @Override 405 | public void removeModelListener(ZVModelListener listener) { 406 | listenerList.remove(ZVModelListener.class, listener); 407 | } 408 | 409 | protected void fireNodeCreated(ZVNode newNode) { 410 | // Guaranteed to return a non-null array 411 | Object[] listeners = listenerList.getListenerList(); 412 | // Process the listeners last to first, notifying 413 | // those that are interested in this event 414 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 415 | if (listeners[i] == ZVModelListener.class) { 416 | ((ZVModelListener) listeners[i + 1]).nodeCreated(newNode); 417 | } 418 | } 419 | } 420 | 421 | protected void fireNodeDeleted(ZVNode oldNode, int oldIndex) { 422 | // Guaranteed to return a non-null array 423 | Object[] listeners = listenerList.getListenerList(); 424 | // Process the listeners last to first, notifying 425 | // those that are interested in this event 426 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 427 | if (listeners[i] == ZVModelListener.class) { 428 | ((ZVModelListener) listeners[i + 1]).nodeDeleted(oldNode, 429 | oldIndex); 430 | } 431 | } 432 | } 433 | 434 | protected void fireNodeDataChanged(ZVNode node) { 435 | // Guaranteed to return a non-null array 436 | Object[] listeners = listenerList.getListenerList(); 437 | // Process the listeners last to first, notifying 438 | // those that are interested in this event 439 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 440 | if (listeners[i] == ZVModelListener.class) { 441 | ((ZVModelListener) listeners[i + 1]).nodeDataChanged(node); 442 | } 443 | } 444 | } 445 | } 446 | -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/model/ZVModelListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.model; 16 | 17 | import java.util.EventListener; 18 | 19 | import net.isammoc.zooviewer.node.ZVNode; 20 | 21 | public interface ZVModelListener extends EventListener { 22 | void nodeCreated(ZVNode newNode); 23 | 24 | void nodeDeleted(ZVNode oldNode, int oldIndex); 25 | 26 | void nodeDataChanged(ZVNode node); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/node/JZVNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.node; 16 | 17 | import java.awt.BorderLayout; 18 | import java.awt.Dimension; 19 | import java.awt.GridBagConstraints; 20 | import java.awt.GridBagLayout; 21 | import java.awt.Insets; 22 | import java.awt.event.ActionEvent; 23 | import java.awt.event.KeyEvent; 24 | import java.beans.PropertyChangeEvent; 25 | import java.beans.PropertyChangeListener; 26 | import java.util.ResourceBundle; 27 | 28 | import javax.swing.AbstractAction; 29 | import javax.swing.Action; 30 | import javax.swing.BorderFactory; 31 | import javax.swing.JButton; 32 | import javax.swing.JLabel; 33 | import javax.swing.JOptionPane; 34 | import javax.swing.JPanel; 35 | import javax.swing.JTextArea; 36 | import javax.swing.JTextField; 37 | import javax.swing.KeyStroke; 38 | import javax.swing.border.BevelBorder; 39 | import javax.swing.border.Border; 40 | import javax.swing.border.TitledBorder; 41 | import javax.swing.event.DocumentEvent; 42 | import javax.swing.event.DocumentListener; 43 | import javax.swing.text.BadLocationException; 44 | 45 | import net.isammoc.zooviewer.model.ZVModel; 46 | import net.isammoc.zooviewer.model.ZVModelListener; 47 | 48 | /** 49 | * Editor panel for a node. 50 | * 51 | * @author franck 52 | */ 53 | public class JZVNode extends JPanel { 54 | private static final Border BEVEL_LOWERED_BORDER = BorderFactory 55 | .createBevelBorder(BevelBorder.LOWERED); 56 | 57 | private static final String ADD_CHILD_NODE_KEY = "btn.add.child"; 58 | private static final String UPDATE_NODE_KEY = "btn.update"; 59 | private static final String DELETE_NODE_KEY = "btn.delete"; 60 | 61 | /** */ 62 | private static final long serialVersionUID = 1L; 63 | 64 | private static final ResourceBundle bundle = ResourceBundle 65 | .getBundle(JZVNode.class.getCanonicalName()); 66 | 67 | private final TitledBorder titleBorder = BorderFactory 68 | .createTitledBorder("-"); 69 | 70 | private ZVNode[] nodes; 71 | private final ZVModel model; 72 | 73 | private final JButton jbNewChild = new JButton(); 74 | private final JButton jbUpdate = new JButton(); 75 | private final JButton jbDelete = new JButton(); 76 | 77 | private final JTextArea taChildData = new JTextArea(); 78 | private final JTextField jtfChildName = new JTextField(); 79 | private final JZVStat jzvStat = new JZVStat(); 80 | private final JTextArea taUpdate = new JTextArea(); 81 | 82 | private Action addChildAction = null; 83 | private Action updateAction = null; 84 | private Action deleteAction = null; 85 | 86 | private JPanel nodePanel = null; 87 | private JPanel deletePanel = null; 88 | private JPanel statsPanel = null; 89 | private JPanel dataPanel = null; 90 | private JPanel newChildPanel = null; 91 | 92 | private final PropertyChangeListener propertyListener = new PropertyChangeListener() { 93 | @Override 94 | public void propertyChange(PropertyChangeEvent evt) { 95 | updateView(); 96 | } 97 | }; 98 | 99 | /** 100 | * Constructs a new editor panel. 101 | * 102 | * @param model 103 | * the model 104 | */ 105 | public JZVNode(ZVModel model) { 106 | super(new BorderLayout()); 107 | 108 | this.model = model; 109 | this.model.addModelListener(new RefreshZVModelListener()); 110 | 111 | // Components 112 | this.taChildData.setBorder(BEVEL_LOWERED_BORDER); 113 | this.taUpdate.setBorder(BEVEL_LOWERED_BORDER); 114 | this.taUpdate.setRows(2); 115 | this.taChildData.setRows(2); 116 | 117 | // Actions 118 | this.jbDelete.setAction(getDeleteAction()); 119 | this.jbNewChild.setAction(getAddChildAction()); 120 | this.jbUpdate.setAction(getUpdateAction()); 121 | 122 | // Main content 123 | this.add(getNodePanel()); 124 | this.updateView(); 125 | 126 | Dimension prefSize = this.jbNewChild.getPreferredSize(); 127 | this.jbDelete.setPreferredSize( prefSize ); 128 | this.jbNewChild.setPreferredSize( prefSize ); 129 | this.jbUpdate.setPreferredSize( prefSize ); 130 | 131 | initListeners(); 132 | } 133 | 134 | /** 135 | * Returns the main node panel. 136 | * 137 | * @return the panel 138 | */ 139 | private JPanel getNodePanel() { 140 | if (nodePanel == null) { 141 | nodePanel = new JPanel(new GridBagLayout()); 142 | 143 | // Sub-panels 144 | int row = 0; 145 | nodePanel.add( 146 | getDeletePanel(), 147 | new GridBagConstraints(0, row, 1, 1, 1, 1, 148 | GridBagConstraints.WEST, GridBagConstraints.BOTH, 149 | new Insets(2, 2, 2, 2), 0, 0)); 150 | nodePanel.add( 151 | getStatsPanel(), 152 | new GridBagConstraints(1, row++, 4, 1, 1, 1, 153 | GridBagConstraints.WEST, GridBagConstraints.BOTH, 154 | new Insets(2, 2, 2, 2), 0, 0)); 155 | 156 | nodePanel.add( 157 | getDataPanel(), 158 | new GridBagConstraints(0, row++, 5, 1, 1, 1, 159 | GridBagConstraints.WEST, GridBagConstraints.BOTH, 160 | new Insets(2, 2, 2, 2), 0, 0)); 161 | 162 | nodePanel.add( 163 | getNewChildPanel(), 164 | new GridBagConstraints(0, row++, 5, 1, 1, 1, 165 | GridBagConstraints.WEST, GridBagConstraints.BOTH, 166 | new Insets(2, 2, 2, 2), 0, 0)); 167 | } 168 | return nodePanel; 169 | } 170 | 171 | private JPanel getDeletePanel() { 172 | if (deletePanel == null) { 173 | deletePanel = new JPanel(new GridBagLayout()); 174 | deletePanel.setBorder(this.titleBorder); 175 | deletePanel.add(this.jbDelete, new GridBagConstraints(0, 0, 1, 1, 176 | 1, 1, GridBagConstraints.SOUTHWEST, 177 | GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0)); 178 | } 179 | return deletePanel; 180 | } 181 | 182 | /** 183 | * Returns the panel displaying the node stats. 184 | * 185 | * @return the panel 186 | */ 187 | private JPanel getStatsPanel() { 188 | if (statsPanel == null) { 189 | statsPanel = new JPanel(new BorderLayout()); 190 | statsPanel.setBorder(BorderFactory.createTitledBorder(bundle 191 | .getString("pnl.stat"))); 192 | statsPanel.add(this.jzvStat); 193 | } 194 | return statsPanel; 195 | } 196 | 197 | private JPanel getDataPanel() { 198 | if (dataPanel == null) { 199 | dataPanel = new JPanel(new GridBagLayout()); 200 | dataPanel.setBorder(BorderFactory.createTitledBorder(bundle 201 | .getString("pnl.data"))); 202 | dataPanel.add(this.jbUpdate, new GridBagConstraints(0, 0, 1, 1, 203 | 0, 0, GridBagConstraints.SOUTHWEST, 204 | GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0)); 205 | dataPanel.add(this.taUpdate, new GridBagConstraints(1, 0, 1, 1, 206 | 1, .5, GridBagConstraints.CENTER, GridBagConstraints.BOTH, 207 | new Insets(2, 2, 2, 2), 0, 0)); 208 | } 209 | return dataPanel; 210 | } 211 | 212 | private JPanel getNewChildPanel() { 213 | if (newChildPanel == null) { 214 | newChildPanel = new JPanel(new GridBagLayout()); 215 | newChildPanel.setBorder(BorderFactory.createTitledBorder(bundle 216 | .getString("pnl.new.child"))); 217 | newChildPanel.add( 218 | new JLabel(bundle.getString("pnl.new.child.lbl.name")), 219 | new GridBagConstraints(0, 0, 1, 1, 0, 0, 220 | GridBagConstraints.WEST, 221 | GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 222 | 2), 0, 0)); 223 | newChildPanel.add(this.jtfChildName, 224 | new GridBagConstraints(1, 0, 1, 1, 1, .2, 225 | GridBagConstraints.NORTHWEST, 226 | GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 227 | 2), 0, 0)); 228 | newChildPanel.add( 229 | new JLabel(bundle.getString("pnl.new.child.lbl.data")), 230 | new GridBagConstraints(0, 1, 1, 1, 0, 0, 231 | GridBagConstraints.NORTHWEST, 232 | GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 233 | 2), 0, 0)); 234 | newChildPanel.add(this.taChildData, new GridBagConstraints(1, 1, 1, 235 | 2, 1, 1, GridBagConstraints.WEST, GridBagConstraints.BOTH, 236 | new Insets(2, 2, 2, 2), 0, 0)); 237 | 238 | newChildPanel.add(this.jbNewChild, new GridBagConstraints(0, 1, 1, 239 | 2, 0, 0, GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, 240 | new Insets(2, 2, 2, 2), 0, 0)); 241 | } 242 | return newChildPanel; 243 | } 244 | 245 | /** 246 | * Returns the 'Add child' action. 247 | * 248 | * @return the action 249 | */ 250 | @SuppressWarnings("serial") 251 | private Action getAddChildAction() { 252 | if (addChildAction == null) { 253 | String actionCommand = bundle.getString(ADD_CHILD_NODE_KEY); 254 | String actionKey = bundle.getString(ADD_CHILD_NODE_KEY + ".action"); 255 | addChildAction = new AbstractAction(actionCommand) { 256 | @Override 257 | public void actionPerformed(ActionEvent e) { 258 | System.out.println("actionPerformed(): action = " 259 | + e.getActionCommand()); 260 | if (checkAction()) { 261 | model.addNode( 262 | nodes[0].getPath() + "/" 263 | + jtfChildName.getText(), taChildData 264 | .getText().getBytes()); 265 | } 266 | } 267 | 268 | private boolean checkAction() { 269 | // No node or several nodes selected 270 | if (nodes == null || nodes.length > 1) { 271 | return false; 272 | } 273 | // Emptry node name 274 | if (jtfChildName.getText().isEmpty()) { 275 | JOptionPane.showMessageDialog(JZVNode.this, 276 | bundle.getString("dlg.error.addWithoutName"), 277 | bundle.getString("dlg.error.title"), 278 | JOptionPane.ERROR_MESSAGE); 279 | return false; 280 | } 281 | // No parent 282 | if (nodes == null || nodes.length != 1) { 283 | JOptionPane.showMessageDialog(JZVNode.this, 284 | bundle.getString("dlg.error.addWithoutParent"), 285 | bundle.getString("dlg.error.title"), 286 | JOptionPane.ERROR_MESSAGE); 287 | return false; 288 | } 289 | return true; 290 | } 291 | }; 292 | addChildAction.putValue(Action.ACTION_COMMAND_KEY, actionKey); 293 | } 294 | return this.addChildAction; 295 | } 296 | 297 | /** 298 | * Returns the 'Update' action. 299 | * 300 | * @return the action 301 | */ 302 | @SuppressWarnings("serial") 303 | private Action getUpdateAction() { 304 | if (updateAction == null) { 305 | String actionCommand = bundle.getString(UPDATE_NODE_KEY); 306 | String actionKey = bundle.getString(UPDATE_NODE_KEY + ".action"); 307 | updateAction = new AbstractAction(actionCommand) { 308 | @Override 309 | public void actionPerformed(ActionEvent e) { 310 | System.out.println("actionPerformed(): action = " 311 | + e.getActionCommand()); 312 | if (checkAction()) { 313 | model.updateData(nodes[0].getPath(), taUpdate 314 | .getText().getBytes()); 315 | } 316 | } 317 | 318 | private boolean checkAction() { 319 | // No node or several nodes selected 320 | if (nodes == null || nodes.length > 1) { 321 | return false; 322 | } 323 | // No parent 324 | if (nodes == null || nodes.length != 1) { 325 | JOptionPane.showMessageDialog(JZVNode.this, bundle 326 | .getString("dlg.error.updateWithoutParent"), 327 | bundle.getString("dlg.error.title"), 328 | JOptionPane.ERROR_MESSAGE); 329 | return false; 330 | } 331 | return true; 332 | } 333 | }; 334 | updateAction.putValue(Action.ACTION_COMMAND_KEY, actionKey); 335 | } 336 | return updateAction; 337 | } 338 | 339 | /** 340 | * Returns the 'Delete node(s)' action. 341 | *

342 | * The action is created and mapped to the [Delete] key stroke 343 | *

344 | * 345 | * @return the action 346 | */ 347 | @SuppressWarnings("serial") 348 | private Action getDeleteAction() { 349 | if (this.deleteAction == null) { 350 | String actionCommand = bundle.getString(DELETE_NODE_KEY); 351 | String actionKey = bundle.getString(DELETE_NODE_KEY + ".action"); 352 | this.deleteAction = new AbstractAction(actionCommand) { 353 | @Override 354 | public void actionPerformed(ActionEvent e) { 355 | System.out.println("actionPerformed(): action = " 356 | + e.getActionCommand()); 357 | if (checkAction()) { 358 | // Checks if several nodes will be deleted 359 | if (nodes.length > 1) { 360 | model.deleteNodes(nodes); 361 | } else { 362 | model.deleteNode(nodes[0]); 363 | } 364 | } 365 | } 366 | 367 | private boolean checkAction() { 368 | // No node selected 369 | if (nodes == null) { 370 | JOptionPane.showMessageDialog(JZVNode.this, bundle 371 | .getString("dlg.error.deleteWithoutSelection"), 372 | bundle.getString("dlg.error.title"), 373 | JOptionPane.ERROR_MESSAGE); 374 | return false; 375 | } 376 | return true; 377 | } 378 | }; 379 | this.deleteAction.putValue(Action.ACTION_COMMAND_KEY, actionKey); 380 | 381 | this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put( 382 | KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), actionKey); 383 | this.getActionMap().put(actionKey, this.deleteAction); 384 | } 385 | return this.deleteAction; 386 | } 387 | 388 | /** 389 | * Defines the list of selected nodes. 390 | * 391 | * @param nodes 392 | * the selected nodes 393 | */ 394 | public void setNodes(ZVNode[] nodes) { 395 | if (this.nodes != null) { 396 | for (int i = 0; i < this.nodes.length; i++) { 397 | this.nodes[i].removePropertyChangeListener( 398 | ZVNode.PROPERTY_EXISTS, this.propertyListener); 399 | } 400 | } 401 | this.nodes = nodes; 402 | if (this.nodes != null) { 403 | for (int i = 0; i < this.nodes.length; i++) { 404 | this.nodes[i].addPropertyChangeListener(ZVNode.PROPERTY_EXISTS, 405 | this.propertyListener); 406 | } 407 | } 408 | this.updateView(); 409 | } 410 | 411 | private void initListeners() { 412 | taUpdate.getDocument().addDocumentListener( new DocumentListener() { 413 | @Override 414 | public void removeUpdate(DocumentEvent e) { 415 | enableAction(e); 416 | } 417 | @Override 418 | public void insertUpdate(DocumentEvent e) { 419 | enableAction(e); 420 | } 421 | @Override 422 | public void changedUpdate(DocumentEvent e) { 423 | enableAction(e); 424 | } 425 | private void enableAction(DocumentEvent e) { 426 | boolean enabled = e.getDocument().getLength() > 0; 427 | getUpdateAction().setEnabled( enabled ); 428 | } 429 | }); 430 | jtfChildName.getDocument().addDocumentListener( new DocumentListener() { 431 | @Override 432 | public void removeUpdate(DocumentEvent e) { 433 | System.out.println(".removeUpdate()"); 434 | enableAction(e); 435 | } 436 | @Override 437 | public void insertUpdate(DocumentEvent e) { 438 | System.out.println(".insertUpdate()"); 439 | enableAction(e); 440 | } 441 | @Override 442 | public void changedUpdate(DocumentEvent e) { 443 | System.out.println(".changedUpdate()"); 444 | enableAction(e); 445 | } 446 | private void enableAction(DocumentEvent e) { 447 | int docLength = e.getDocument().getLength(); 448 | boolean enabled; 449 | try { 450 | enabled = ( docLength > 0 ) 451 | && !e.getDocument().getText(0, docLength).trim().equals(""); 452 | getAddChildAction().setEnabled( enabled ); 453 | } catch (BadLocationException e1) { 454 | // TODO Auto-generated catch block 455 | e1.printStackTrace(); 456 | } 457 | } 458 | }); 459 | } 460 | 461 | /** 462 | * Updates the view. 463 | *

464 | * If a node is selected, its data & stats are displayed. If no node is 465 | * selected (or several nodes are selected), the view is cleared. 466 | *

467 | */ 468 | private void updateView() { 469 | if (this.nodes == null || this.nodes.length > 1 470 | || !this.nodes[0].exists()) { 471 | this.titleBorder.setTitle("-"); 472 | this.jzvStat.setStat(null); 473 | this.taUpdate.setText(""); 474 | this.taChildData.setText(""); 475 | this.jbUpdate.setEnabled(false); 476 | this.jbNewChild.setEnabled(false); 477 | this.jbDelete.setEnabled(this.nodes != null); 478 | } else { 479 | this.titleBorder.setTitle(this.nodes[0].getPath()); 480 | this.jzvStat.setStat(this.nodes[0].getStat()); 481 | byte[] data = this.nodes[0].getData(); 482 | this.taUpdate.setText(new String(data == null ? "null".getBytes() 483 | : data)); 484 | this.taChildData.setText(""); 485 | this.jbUpdate.setEnabled( !this.taUpdate.getText().trim().equals("") ); 486 | this.jbNewChild.setEnabled( !this.jtfChildName.getText().trim().equals("") ); 487 | this.jbDelete.setEnabled(true); 488 | } 489 | this.repaint(); 490 | } 491 | 492 | /** 493 | * Class managing events in order to update the view. 494 | */ 495 | private final class RefreshZVModelListener implements ZVModelListener { 496 | @Override 497 | public void nodeDeleted(ZVNode oldNode, int oldIndex) { 498 | if (nodes != null) { 499 | for (int i = 0; i < nodes.length; i++) { 500 | if ((nodes[i] == oldNode) 501 | || (nodes[i] == model.getParent(oldNode))) { 502 | updateView(); 503 | break; 504 | } 505 | } 506 | } 507 | } 508 | 509 | @Override 510 | public void nodeDataChanged(ZVNode node) { 511 | boolean updateView = false; 512 | if (nodes != null) { 513 | for (int i = 0; i < nodes.length; i++) { 514 | if ((nodes[i] == node)) { 515 | updateView = true; 516 | } 517 | } 518 | } 519 | if (updateView) { 520 | updateView(); 521 | } 522 | } 523 | 524 | @Override 525 | public void nodeCreated(ZVNode newNode) { 526 | boolean updateView = false; 527 | if (nodes != null) { 528 | for (int i = 0; i < nodes.length; i++) { 529 | if ((nodes[i] == newNode)) { 530 | updateView = true; 531 | } 532 | } 533 | } 534 | if (updateView) { 535 | updateView(); 536 | } 537 | } 538 | } 539 | } -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/node/JZVStat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.node; 16 | 17 | import java.awt.BorderLayout; 18 | import java.awt.GridLayout; 19 | import java.text.DateFormat; 20 | import java.util.Date; 21 | 22 | import javax.swing.BoxLayout; 23 | import javax.swing.JLabel; 24 | import javax.swing.JPanel; 25 | 26 | import org.apache.zookeeper.data.Stat; 27 | 28 | public class JZVStat extends JPanel { 29 | /** */ 30 | private static final long serialVersionUID = 1L; 31 | 32 | private final JStatView aversion = new JStatView("aversion :"); 33 | private final JStatView ctime = new JStatView("ctime :"); 34 | private final JStatView cversion = new JStatView("cversion :"); 35 | private final JStatView czxid = new JStatView("czxid :"); 36 | private final JStatView dataLength = new JStatView("data length :"); 37 | private final JStatView ephemeralOwner = new JStatView("ephemeral owner :"); 38 | private final JStatView mtime = new JStatView("mtime :"); 39 | private final JStatView mzxid = new JStatView("mzxid :"); 40 | private final JStatView numChildren = new JStatView("numChildren :"); 41 | private final JStatView pzxid = new JStatView("pzxid :"); 42 | private final JStatView version = new JStatView("version :"); 43 | 44 | private final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance( 45 | DateFormat.SHORT, DateFormat.SHORT, this.getLocale()); 46 | 47 | private class JStatView extends JPanel { 48 | /** */ 49 | private static final long serialVersionUID = 1L; 50 | private final JLabel jlValue = new JLabel(); 51 | 52 | public JStatView(String label) { 53 | super(new BorderLayout(2, 0)); 54 | this.add(new JLabel(label), BorderLayout.WEST); 55 | this.add(this.jlValue); 56 | } 57 | 58 | public void setValue(String value) { 59 | this.jlValue.setText(value); 60 | this.doLayout(); 61 | } 62 | } 63 | 64 | public JZVStat() { 65 | super(); 66 | this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); 67 | JPanel statsPane = new JPanel(new GridLayout(0, 2)); 68 | statsPane.add(this.ctime); 69 | statsPane.add(this.mtime); 70 | statsPane.add(this.version); 71 | statsPane.add(this.aversion); 72 | statsPane.add(this.cversion); 73 | statsPane.add(this.czxid); 74 | statsPane.add(this.mzxid); 75 | statsPane.add(this.pzxid); 76 | statsPane.add(this.dataLength); 77 | statsPane.add(this.ephemeralOwner); 78 | statsPane.add(this.numChildren); 79 | this.add(statsPane); 80 | } 81 | 82 | private JPanel createStatPane(JStatView sView1, JStatView sView2, 83 | JStatView sView3, JStatView sView4) { 84 | JPanel statsPane = new JPanel(new GridLayout(3, 1)); 85 | if (sView1 != null) { 86 | statsPane.add(sView1); 87 | } 88 | if (sView2 != null) { 89 | statsPane.add(sView2); 90 | } 91 | if (sView3 != null) { 92 | statsPane.add(sView3); 93 | } 94 | if (sView4 != null) { 95 | statsPane.add(sView4); 96 | } 97 | return statsPane; 98 | } 99 | 100 | public void setStat(Stat stat) { 101 | if (stat == null) { 102 | System.out.println("stat null"); 103 | this.aversion.setValue(""); 104 | this.ctime.setValue(""); 105 | this.cversion.setValue(""); 106 | this.czxid.setValue(""); 107 | this.dataLength.setValue(""); 108 | this.ephemeralOwner.setValue(""); 109 | this.mtime.setValue(""); 110 | this.mzxid.setValue(""); 111 | this.numChildren.setValue(""); 112 | this.pzxid.setValue(""); 113 | this.version.setValue(""); 114 | } else { 115 | System.out.println("stat = " + stat); 116 | 117 | this.aversion.setValue(String.valueOf(stat.getAversion())); 118 | this.ctime.setValue(this.DATE_FORMAT.format(new Date(stat 119 | .getCtime()))); 120 | this.cversion.setValue(String.valueOf(stat.getCversion())); 121 | this.czxid.setValue(String.valueOf(stat.getCzxid())); 122 | this.dataLength.setValue(String.valueOf(stat.getDataLength())); 123 | this.ephemeralOwner.setValue(String.valueOf(stat 124 | .getEphemeralOwner())); 125 | this.mtime.setValue(this.DATE_FORMAT.format(new Date(stat 126 | .getMtime()))); 127 | this.mzxid.setValue(String.valueOf(stat.getMzxid())); 128 | this.numChildren.setValue(String.valueOf(stat.getNumChildren())); 129 | this.pzxid.setValue(String.valueOf(stat.getPzxid())); 130 | this.version.setValue(String.valueOf(stat.getVersion())); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/node/ZVNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.node; 16 | 17 | import java.beans.PropertyChangeListener; 18 | 19 | import org.apache.zookeeper.data.Stat; 20 | 21 | /** 22 | * A ZVNode is a data fit requierements to be a ZooKeeper node. 23 | */ 24 | public interface ZVNode { 25 | final String PROPERTY_DATA = "data"; 26 | final String PROPERTY_EXISTS = "exists"; 27 | final String PROPERTY_CHILDREN = "children"; 28 | final String PROPERTY_STAT = "stat"; 29 | 30 | /** 31 | * Returns this node's path. 32 | * @return the path 33 | */ 34 | String getPath(); 35 | 36 | /** 37 | * Returns this node's name. 38 | * @return the name 39 | */ 40 | String getName(); 41 | 42 | /** 43 | * Returns this node's data. 44 | * @return the data 45 | */ 46 | byte[] getData(); 47 | 48 | /** 49 | * Returns this node's stats. 50 | * @return the stats 51 | */ 52 | Stat getStat(); 53 | 54 | /** 55 | * Checks if this node exists in the ZooKeeper model. 56 | * @return 57 | */ 58 | boolean exists(); 59 | 60 | /** 61 | * Adds a {@link PropertyChangeListener} to this node's listeners list. 62 | * @param listener the listener 63 | */ 64 | void addPropertyChangeListener(PropertyChangeListener listener); 65 | 66 | /** 67 | * Adds a {@link PropertyChangeListener} to this node's listeners list. 68 | * 69 | * @param propertyName the property name 70 | * @param listener the listener 71 | */ 72 | void addPropertyChangeListener(String propertyName, 73 | PropertyChangeListener listener); 74 | 75 | /** 76 | * Remove the specified {@link PropertyChangeListener} from this node's listeners list. 77 | * @param listener the listener 78 | */ 79 | void removePropertyChangeListener(PropertyChangeListener listener); 80 | 81 | /** 82 | * Remove the specified {@link PropertyChangeListener} from this node's listeners list. 83 | * @param the property name 84 | * @param listener the listener 85 | */ 86 | void removePropertyChangeListener(String propertyName, 87 | PropertyChangeListener listener); 88 | } -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/node/ZVNodeImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.node; 16 | 17 | import java.beans.PropertyChangeListener; 18 | import java.beans.PropertyChangeSupport; 19 | import java.util.Arrays; 20 | 21 | import org.apache.zookeeper.data.Stat; 22 | 23 | public class ZVNodeImpl implements ZVNode { 24 | 25 | private final String path; 26 | private final String name; 27 | private boolean exists; 28 | private byte[] data; 29 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 30 | private Stat stat; 31 | 32 | public ZVNodeImpl(String path) { 33 | this.path = path; 34 | if ("/".equals(path)) { 35 | this.name = "/"; 36 | } else { 37 | this.name = path.substring(path.lastIndexOf("/") + 1); 38 | } 39 | this.exists = false; 40 | } 41 | 42 | public ZVNodeImpl(String path, byte[] data) { 43 | this.path = path; 44 | if ("/".equals(path)) { 45 | this.name = "/"; 46 | } else { 47 | this.name = path.substring(path.lastIndexOf("/") + 1); 48 | } 49 | this.data = (data == null ? null : Arrays.copyOf(data, data.length)); 50 | this.exists = true; 51 | } 52 | 53 | @Override 54 | public String getPath() { 55 | return this.path; 56 | } 57 | 58 | @Override 59 | public String getName() { 60 | return this.name; 61 | } 62 | 63 | @Override 64 | public byte[] getData() { 65 | if (this.data == null) { 66 | return null; 67 | } else { 68 | return Arrays.copyOf(this.data, this.data.length); 69 | } 70 | } 71 | 72 | public void setData(byte[] data) { 73 | if (!Arrays.equals(this.data, data)) { 74 | byte[] old = this.data; 75 | this.data = (data == null ? null : Arrays.copyOf(data, data.length)); 76 | this.pcs.firePropertyChange(PROPERTY_DATA, old, data); 77 | } 78 | } 79 | 80 | @Override 81 | public boolean exists() { 82 | return this.exists; 83 | } 84 | 85 | public void setExists(boolean newExists) { 86 | if (newExists != this.exists) { 87 | this.exists = newExists; 88 | this.pcs.firePropertyChange(PROPERTY_EXISTS, !this.exists, 89 | this.exists); 90 | } 91 | } 92 | 93 | @Override 94 | public void addPropertyChangeListener(PropertyChangeListener listener) { 95 | this.pcs.addPropertyChangeListener(listener); 96 | } 97 | 98 | @Override 99 | public void removePropertyChangeListener(PropertyChangeListener listener) { 100 | this.pcs.removePropertyChangeListener(listener); 101 | } 102 | 103 | @Override 104 | public void addPropertyChangeListener(String propertyName, 105 | PropertyChangeListener listener) { 106 | this.pcs.addPropertyChangeListener(propertyName, listener); 107 | } 108 | 109 | @Override 110 | public void removePropertyChangeListener(String propertyName, 111 | PropertyChangeListener listener) { 112 | this.pcs.removePropertyChangeListener(propertyName, listener); 113 | } 114 | 115 | @Override 116 | public boolean equals(Object obj) { 117 | if (obj == null) { 118 | return false; 119 | } 120 | if (this.getClass() != obj.getClass()) { 121 | return false; 122 | } 123 | ZVNodeImpl other = (ZVNodeImpl) obj; 124 | return this.path.equals(other.path); 125 | } 126 | 127 | @Override 128 | public int hashCode() { 129 | return this.path.hashCode(); 130 | } 131 | 132 | @Override 133 | public String toString() { 134 | return String.format("ZVNodeImpl[path='%s', " + this.exists 135 | + ", length='%d']", this.path, (this.data == null ? -1 136 | : this.data.length)); 137 | } 138 | 139 | @Override 140 | public Stat getStat() { 141 | return this.stat == null ? null : copyStat(this.stat); 142 | } 143 | 144 | public void setStat(Stat stat) { 145 | if (this.stat == null ? stat != null : !this.stat.equals(stat)) { 146 | Stat old = this.stat; 147 | this.stat = stat == null ? null : copyStat(stat); 148 | this.pcs.firePropertyChange(PROPERTY_STAT, old, stat); 149 | } 150 | } 151 | 152 | private static Stat copyStat(Stat stat) { 153 | return new Stat(stat.getCzxid(), stat.getMzxid(), stat.getCtime(), 154 | stat.getMtime(), stat.getVersion(), stat.getCversion(), 155 | stat.getAversion(), stat.getEphemeralOwner(), 156 | stat.getDataLength(), stat.getNumChildren(), stat.getPzxid()); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/tree/JZVTree.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.tree; 16 | 17 | import javax.swing.JTree; 18 | 19 | import net.isammoc.zooviewer.model.ZVModel; 20 | import net.isammoc.zooviewer.node.ZVNode; 21 | 22 | public class JZVTree extends JTree { 23 | /** */ 24 | private static final long serialVersionUID = 1L; 25 | 26 | public JZVTree(ZVModel model) { 27 | super(new ZVTreeModel(model)); 28 | } 29 | 30 | public JZVTree(ZVTreeModel model) { 31 | super(model); 32 | } 33 | 34 | @Override 35 | public String convertValueToText(Object value, boolean selected, 36 | boolean expanded, boolean leaf, int row, boolean hasFocus) { 37 | if (value instanceof ZVNode) { 38 | return ((ZVNode) value).getName(); 39 | } 40 | return super.convertValueToText(value, selected, expanded, leaf, row, 41 | hasFocus); 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/net/isammoc/zooviewer/tree/ZVTreeModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU Lesser General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License 13 | * along with this program. If not, see . 14 | */ 15 | package net.isammoc.zooviewer.tree; 16 | 17 | import java.util.StringTokenizer; 18 | 19 | import javax.swing.event.EventListenerList; 20 | import javax.swing.event.TreeModelEvent; 21 | import javax.swing.event.TreeModelListener; 22 | import javax.swing.tree.TreeModel; 23 | import javax.swing.tree.TreePath; 24 | 25 | import net.isammoc.zooviewer.model.ZVModel; 26 | import net.isammoc.zooviewer.model.ZVModelListener; 27 | import net.isammoc.zooviewer.node.ZVNode; 28 | 29 | public class ZVTreeModel implements TreeModel { 30 | /** Listeners. */ 31 | protected EventListenerList listenerList = new EventListenerList(); 32 | private final ZVModel model; 33 | 34 | public ZVTreeModel(ZVModel model) { 35 | this.model = model; 36 | model.addModelListener(new ZVModelListener() { 37 | 38 | @Override 39 | public void nodeDeleted(ZVNode oldNode, int oldIndex) { 40 | System.out.println("nodeDeleted : " + oldNode); 41 | ZVTreeModel.this.fireTreeNodesRemoved(this, ZVTreeModel.this 42 | .getTreePath(oldNode).getParentPath(), 43 | new int[] { oldIndex }, new Object[] { oldNode }); 44 | } 45 | 46 | @Override 47 | public void nodeDataChanged(ZVNode node) { 48 | System.out.println("nodeDataChanged : " + node); 49 | // FLE+ 50 | System.out.println("nodeDataChanged : " + node); 51 | TreePath parentPath = ZVTreeModel.this.getTreePath(node) 52 | .getParentPath(); 53 | int index = ZVTreeModel.this.getIndexOfChild( 54 | parentPath.getLastPathComponent(), node); 55 | ZVTreeModel.this.fireTreeNodesChanged(this, 56 | parentPath.getPath(), new int[] { index }, 57 | new Object[] { node }); 58 | } 59 | 60 | @Override 61 | public void nodeCreated(ZVNode newNode) { 62 | System.out.println("nodeCreated : " + newNode); 63 | if (newNode == ZVTreeModel.this.getRoot()) { 64 | ZVTreeModel.this.fireTreeStructureChanged(this, 65 | new TreePath(newNode)); 66 | } else { 67 | try { 68 | TreePath treePath = ZVTreeModel.this.getTreePath( 69 | newNode).getParentPath(); 70 | ZVTreeModel.this.fireTreeNodesInserted(this, treePath, 71 | new int[] { ZVTreeModel.this.getIndexOfChild( 72 | treePath.getLastPathComponent(), 73 | newNode) }, new Object[] { newNode }); 74 | // System.out.println("fire done"); 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | } 80 | }); 81 | } 82 | 83 | public TreePath getTreePath(ZVNode node) { 84 | String path = node.getPath(); 85 | TreePath treePath = new TreePath(this.model.getNode("/")); 86 | String currentPath = ""; 87 | StringTokenizer st = new StringTokenizer(path, "/", false); 88 | while (st.hasMoreTokens()) { 89 | currentPath += "/" + st.nextToken(); 90 | treePath = treePath.pathByAddingChild(this.model 91 | .getNode(currentPath)); 92 | } 93 | return treePath; 94 | } 95 | 96 | @Override 97 | public ZVNode getRoot() { 98 | return this.model.getNode("/"); 99 | } 100 | 101 | @Override 102 | public ZVNode getChild(Object parent, int index) { 103 | if (!(parent instanceof ZVNode)) { 104 | throw new IllegalArgumentException("parent must be a ZVNode"); 105 | } 106 | 107 | return this.model.getChildren((ZVNode) parent).get(index); 108 | } 109 | 110 | @Override 111 | public int getChildCount(Object parent) { 112 | if (!(parent instanceof ZVNode)) { 113 | throw new IllegalArgumentException("parent must be a ZVNode"); 114 | } 115 | 116 | return this.model.getChildren((ZVNode) parent).size(); 117 | } 118 | 119 | @Override 120 | public boolean isLeaf(Object node) { 121 | if (!(node instanceof ZVNode)) { 122 | throw new IllegalArgumentException("node must be a ZVNode"); 123 | } 124 | 125 | return this.model.getChildren((ZVNode) node).size() == 0; 126 | } 127 | 128 | @Override 129 | public void valueForPathChanged(TreePath path, Object newValue) { 130 | throw new UnsupportedOperationException("Can't change data"); 131 | } 132 | 133 | @Override 134 | public int getIndexOfChild(Object parent, Object child) { 135 | if (!(parent instanceof ZVNode)) { 136 | throw new IllegalArgumentException("parent must be a ZVNode"); 137 | } 138 | if (!(child instanceof ZVNode)) { 139 | throw new IllegalArgumentException("child must be a ZVNode"); 140 | } 141 | return this.model.getChildren((ZVNode) parent).indexOf(child); 142 | } 143 | 144 | /** 145 | * Adds a listener for the TreeModelEvent posted after the tree changes. 146 | * 147 | * @see #removeTreeModelListener 148 | * @param l 149 | * the listener to add 150 | */ 151 | @Override 152 | public void addTreeModelListener(TreeModelListener l) { 153 | this.listenerList.add(TreeModelListener.class, l); 154 | } 155 | 156 | /** 157 | * Removes a listener previously added with addTreeModelListener(). 158 | * 159 | * @see #addTreeModelListener 160 | * @param l 161 | * the listener to remove 162 | */ 163 | @Override 164 | public void removeTreeModelListener(TreeModelListener l) { 165 | this.listenerList.remove(TreeModelListener.class, l); 166 | } 167 | 168 | /** 169 | * Notifies all listeners that have registered interest for notification on 170 | * this event type. The event instance is lazily created using the 171 | * parameters passed into the fire method. 172 | * 173 | * @param source 174 | * the node being changed 175 | * @param path 176 | * the path to the root node 177 | * @param childIndices 178 | * the indices of the changed elements 179 | * @param children 180 | * the changed elements 181 | * @see EventListenerList 182 | */ 183 | protected void fireTreeNodesChanged(Object source, Object[] path, 184 | int[] childIndices, Object[] children) { 185 | // Guaranteed to return a non-null array 186 | Object[] listeners = this.listenerList.getListenerList(); 187 | TreeModelEvent e = null; 188 | // Process the listeners last to first, notifying 189 | // those that are interested in this event 190 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 191 | if (listeners[i] == TreeModelListener.class) { 192 | // Lazily create the event: 193 | if (e == null) { 194 | e = new TreeModelEvent(source, path, childIndices, children); 195 | } 196 | ((TreeModelListener) listeners[i + 1]).treeNodesChanged(e); 197 | } 198 | } 199 | } 200 | 201 | /** 202 | * Notifies all listeners that have registered interest for notification on 203 | * this event type. The event instance is lazily created using the 204 | * parameters passed into the fire method. 205 | * 206 | * @param source 207 | * the node where new elements are being inserted 208 | * @param path 209 | * the path to the root node 210 | * @param childIndices 211 | * the indices of the new elements 212 | * @param children 213 | * the new elements 214 | * @see EventListenerList 215 | */ 216 | protected void fireTreeNodesInserted(Object source, TreePath path, 217 | int[] childIndices, Object[] children) { 218 | // Guaranteed to return a non-null array 219 | Object[] listeners = this.listenerList.getListenerList(); 220 | TreeModelEvent e = null; 221 | // Process the listeners last to first, notifying 222 | // those that are interested in this event 223 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 224 | if (listeners[i] == TreeModelListener.class) { 225 | // Lazily create the event: 226 | if (e == null) { 227 | e = new TreeModelEvent(source, path, childIndices, children); 228 | } 229 | ((TreeModelListener) listeners[i + 1]).treeNodesInserted(e); 230 | } 231 | } 232 | } 233 | 234 | /** 235 | * Notifies all listeners that have registered interest for notification on 236 | * this event type. The event instance is lazily created using the 237 | * parameters passed into the fire method. 238 | * 239 | * @param source 240 | * the node where elements are being removed 241 | * @param path 242 | * the path to the root node 243 | * @param childIndices 244 | * the indices of the removed elements 245 | * @param children 246 | * the removed elements 247 | * @see EventListenerList 248 | */ 249 | protected void fireTreeNodesRemoved(Object source, TreePath treePath, 250 | int[] indexes, Object[] objects) { 251 | // Guaranteed to return a non-null array 252 | Object[] listeners = this.listenerList.getListenerList(); 253 | TreeModelEvent e = null; 254 | // Process the listeners last to first, notifying 255 | // those that are interested in this event 256 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 257 | if (listeners[i] == TreeModelListener.class) { 258 | // Lazily create the event: 259 | if (e == null) { 260 | e = new TreeModelEvent(source, treePath, indexes, objects); 261 | } 262 | ((TreeModelListener) listeners[i + 1]).treeNodesRemoved(e); 263 | } 264 | } 265 | } 266 | 267 | /** 268 | * Notifies all listeners that have registered interest for notification on 269 | * this event type. The event instance is lazily created using the 270 | * parameters passed into the fire method. 271 | * 272 | * @param source 273 | * the node where the tree model has changed 274 | * @param path 275 | * the path to the root node 276 | * @param childIndices 277 | * the indices of the affected elements 278 | * @param children 279 | * the affected elements 280 | * @see EventListenerList 281 | */ 282 | protected void fireTreeStructureChanged(Object source, Object[] path, 283 | int[] childIndices, Object[] children) { 284 | // Guaranteed to return a non-null array 285 | Object[] listeners = this.listenerList.getListenerList(); 286 | TreeModelEvent e = null; 287 | // Process the listeners last to first, notifying 288 | // those that are interested in this event 289 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 290 | if (listeners[i] == TreeModelListener.class) { 291 | // Lazily create the event: 292 | if (e == null) { 293 | e = new TreeModelEvent(source, path, childIndices, children); 294 | } 295 | ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e); 296 | } 297 | } 298 | } 299 | 300 | /* 301 | * Notifies all listeners that have registered interest for notification on 302 | * this event type. The event instance is lazily created using the 303 | * parameters passed into the fire method. 304 | * 305 | * @param source the node where the tree model has changed 306 | * 307 | * @param path the path to the root node 308 | * 309 | * @see EventListenerList 310 | */ 311 | private void fireTreeStructureChanged(Object source, TreePath path) { 312 | // Guaranteed to return a non-null array 313 | Object[] listeners = this.listenerList.getListenerList(); 314 | TreeModelEvent e = null; 315 | // Process the listeners last to first, notifying 316 | // those that are interested in this event 317 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 318 | if (listeners[i] == TreeModelListener.class) { 319 | // Lazily create the event: 320 | if (e == null) { 321 | e = new TreeModelEvent(source, path); 322 | } 323 | ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e); 324 | } 325 | } 326 | } 327 | 328 | } 329 | -------------------------------------------------------------------------------- /src/main/resources/net/isammoc/zooviewer/App.properties: -------------------------------------------------------------------------------- 1 | start.connection.title=ZooKeeper server connection 2 | start.connection.message=Enter the connection string 3 | start.connection.aborted.message=Connection aborted by user. 4 | -------------------------------------------------------------------------------- /src/main/resources/net/isammoc/zooviewer/node/JZVNode.properties: -------------------------------------------------------------------------------- 1 | btn.delete=Delete 2 | btn.delete.action=delete.node 3 | btn.add.child=Add child 4 | btn.add.child.action=add.child 5 | btn.update=Update 6 | btn.update.action=update.node 7 | pnl.stat=Stat 8 | pnl.data=Data 9 | pnl.new.child=New child 10 | pnl.new.child.lbl.name=Name : 11 | pnl.new.child.lbl.data=Data : 12 | dlg.error.addWithoutName=Can't add a node without name 13 | dlg.error.deleteWithoutSelection=Cannotr update node without selection 14 | dlg.error.title=Error --------------------------------------------------------------------------------