diff --git a/config.cfg b/config.cfg index ec7154f..82fda02 100644 --- a/config.cfg +++ b/config.cfg @@ -1,3 +1,7 @@ +#Whether to enable the properties description feature, default false +#showDesc=true +#Set the number of records to display history, default 50 +#historyLimit=500 #Server Port serverPort=9090 #Comma seperated list of all the zookeeper servers diff --git a/src/main/java/com/deem/zkui/Main.java b/src/main/java/com/deem/zkui/Main.java index 998c732..721d87b 100644 --- a/src/main/java/com/deem/zkui/Main.java +++ b/src/main/java/com/deem/zkui/Main.java @@ -18,10 +18,6 @@ package com.deem.zkui; import com.deem.zkui.dao.Dao; -import java.io.File; -import java.io.FileInputStream; -import java.util.Date; -import java.util.Properties; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.Connector; @@ -41,6 +37,11 @@ import org.eclipse.jetty.webapp.WebAppContext; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.util.Date; +import java.util.Properties; + public class Main { private final static org.slf4j.Logger logger = LoggerFactory.getLogger(Main.class); @@ -86,8 +87,9 @@ public static void main(String[] args) throws Exception { HttpConfiguration http_config = new HttpConfiguration(); http_config.setSecureScheme("https"); http_config.setSecurePort(Integer.parseInt(globalProps.getProperty("serverPort"))); - - if (globalProps.getProperty("https").equals("true")) { + + String httpsProp = globalProps.getProperty("https"); + if (httpsProp != null && httpsProp.equals("true")) { File keystoreFile = new File(globalProps.getProperty("keystoreFile")); SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); @@ -100,7 +102,8 @@ public static void main(String[] args) throws Exception { https.setPort(Integer.parseInt(globalProps.getProperty("serverPort"))); server.setConnectors(new Connector[]{https}); } else { - if(globalProps.getProperty("X-Forwarded-For").equals("true")) { + String property = globalProps.getProperty("X-Forwarded-For"); + if(property != null && property.equals("true")) { http_config.addCustomizer(new org.eclipse.jetty.server.ForwardedRequestCustomizer()); } ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(http_config)); diff --git a/src/main/java/com/deem/zkui/controller/ChangeLog.java b/src/main/java/com/deem/zkui/controller/ChangeLog.java index 5dd8dae..77bb8f8 100644 --- a/src/main/java/com/deem/zkui/controller/ChangeLog.java +++ b/src/main/java/com/deem/zkui/controller/ChangeLog.java @@ -51,6 +51,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t List historyLst = dao.fetchHistoryRecords(); templateParam.put("historyLst", historyLst); templateParam.put("historyNode", ""); + templateParam.put("historyLimit", globalProps.getProperty("historyLimit")); ServletUtil.INSTANCE.renderHtml(request, response, templateParam, "history.ftl.html"); } catch (TemplateException ex) { logger.error(Arrays.toString(ex.getStackTrace())); @@ -74,6 +75,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) historyLst = dao.fetchHistoryRecordsByNode("%" + historyNode + "%"); templateParam.put("historyLst", historyLst); templateParam.put("historyNode", historyNode); + templateParam.put("historyLimit", globalProps.getProperty("historyLimit")); ServletUtil.INSTANCE.renderHtml(request, response, templateParam, "history.ftl.html"); } else { diff --git a/src/main/java/com/deem/zkui/controller/Export.java b/src/main/java/com/deem/zkui/controller/Export.java index 8564102..9ae195b 100644 --- a/src/main/java/com/deem/zkui/controller/Export.java +++ b/src/main/java/com/deem/zkui/controller/Export.java @@ -17,23 +17,25 @@ */ package com.deem.zkui.controller; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Date; -import java.util.Properties; -import java.util.Set; +import com.deem.zkui.utils.ServletUtil; +import com.deem.zkui.utils.ZooKeeperUtil; +import com.deem.zkui.vo.LeafBean; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooKeeper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.zookeeper.KeeperException; -import com.deem.zkui.utils.ServletUtil; -import com.deem.zkui.utils.ZooKeeperUtil; -import com.deem.zkui.vo.LeafBean; +import java.io.IOException; +import java.io.PrintWriter; import java.util.Arrays; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.Date; +import java.util.Properties; +import java.util.Set; @SuppressWarnings("serial") @WebServlet(urlPatterns = {"/export"}) @@ -53,10 +55,12 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t if (authRole == null) { authRole = ZooKeeperUtil.ROLE_USER; } + String zkPath = request.getParameter("zkPath"); StringBuilder output = new StringBuilder(); output.append("#App Config Dashboard (ACD) dump created on :").append(new Date()).append("\n"); - Set leaves = ZooKeeperUtil.INSTANCE.exportTree(zkPath, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps), authRole); + ZooKeeper zk = ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps); + Set leaves = ZooKeeperUtil.INSTANCE.exportTree(zkPath, zk, authRole); for (LeafBean leaf : leaves) { output.append(leaf.getPath()).append('=').append(leaf.getName()).append('=').append(ServletUtil.INSTANCE.externalizeNodeValue(leaf.getValue())).append('\n'); }// for all leaves diff --git a/src/main/java/com/deem/zkui/controller/Home.java b/src/main/java/com/deem/zkui/controller/Home.java index c02afb4..5338890 100644 --- a/src/main/java/com/deem/zkui/controller/Home.java +++ b/src/main/java/com/deem/zkui/controller/Home.java @@ -23,6 +23,16 @@ import com.deem.zkui.vo.LeafBean; import com.deem.zkui.vo.ZKNode; import freemarker.template.TemplateException; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooKeeper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; @@ -30,15 +40,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooKeeper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @SuppressWarnings("serial") @WebServlet(urlPatterns = {"/home"}, loadOnStartup = 1) @@ -51,8 +52,10 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t logger.debug("Home Get Action!"); try { Properties globalProps = (Properties) this.getServletContext().getAttribute("globalProps"); + Dao dao = new Dao(globalProps); String zkServer = globalProps.getProperty("zkServer"); String[] zkServerLst = zkServer.split(","); + boolean showDesc = "true".equalsIgnoreCase(globalProps.getProperty("showDesc", "false")); Map templateParam = new HashMap<>(); String zkPath = request.getParameter("zkPath"); @@ -87,9 +90,14 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t } } + if (showDesc) { + dao.putDescription(leafLst); + } + templateParam.put("displayPath", displayPath); templateParam.put("parentPath", parentPath); templateParam.put("currentPath", currentPath); + templateParam.put("showDesc", showDesc); templateParam.put("nodeLst", nodeLst); templateParam.put("leafLst", leafLst); templateParam.put("breadCrumbLst", displayPath.split("/")); @@ -119,6 +127,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) String action = request.getParameter("action"); String currentPath = request.getParameter("currentPath"); String displayPath = request.getParameter("displayPath"); + String newDescription = request.getParameter("newDescription"); String newProperty = request.getParameter("newProperty"); String newValue = request.getParameter("newValue"); String newNode = request.getParameter("newNode"); @@ -143,9 +152,10 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) if (!newProperty.equals("") && !currentPath.equals("") && authRole.equals(ZooKeeperUtil.ROLE_ADMIN)) { //Save the new node. ZooKeeperUtil.INSTANCE.createNode(currentPath, newProperty, newValue, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps)); + dao.insertDescription(currentPath + '/' + newProperty, newDescription); request.getSession().setAttribute("flashMsg", "Property Saved!"); if (ZooKeeperUtil.INSTANCE.checkIfPwdField(newProperty)) { - newValue = ZooKeeperUtil.INSTANCE.SOPA_PIPA; + newValue = ZooKeeperUtil.SOPA_PIPA; } dao.insertHistory((String) request.getSession().getAttribute("authName"), request.getRemoteAddr(), "Saving Property: " + currentPath + "," + newProperty + "=" + newValue); } @@ -155,9 +165,10 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) if (!newProperty.equals("") && !currentPath.equals("") && authRole.equals(ZooKeeperUtil.ROLE_ADMIN)) { //Save the new node. ZooKeeperUtil.INSTANCE.setPropertyValue(currentPath, newProperty, newValue, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps)); + dao.insertDescription(currentPath + '/' + newProperty, newDescription); request.getSession().setAttribute("flashMsg", "Property Updated!"); if (ZooKeeperUtil.INSTANCE.checkIfPwdField(newProperty)) { - newValue = ZooKeeperUtil.INSTANCE.SOPA_PIPA; + newValue = ZooKeeperUtil.SOPA_PIPA; } dao.insertHistory((String) request.getSession().getAttribute("authName"), request.getRemoteAddr(), "Updating Property: " + currentPath + "," + newProperty + "=" + newValue); } @@ -178,6 +189,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) request.getSession().setAttribute("flashMsg", "Delete Completed!"); dao.insertHistory((String) request.getSession().getAttribute("authName"), request.getRemoteAddr(), "Deleting Property: " + delPropLst.toString()); } + dao.deleteDescription(propChkGroup); } if (nodeChkGroup != null) { for (String node : nodeChkGroup) { @@ -186,6 +198,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) request.getSession().setAttribute("flashMsg", "Delete Completed!"); dao.insertHistory((String) request.getSession().getAttribute("authName"), request.getRemoteAddr(), "Deleting Nodes: " + delNodeLst.toString()); } + dao.deleteDescriptionByNode(nodeChkGroup); } } diff --git a/src/main/java/com/deem/zkui/controller/Import.java b/src/main/java/com/deem/zkui/controller/Import.java index e1e018c..ce7e938 100644 --- a/src/main/java/com/deem/zkui/controller/Import.java +++ b/src/main/java/com/deem/zkui/controller/Import.java @@ -17,6 +17,23 @@ */ package com.deem.zkui.controller; +import com.deem.zkui.dao.Dao; +import com.deem.zkui.utils.ServletUtil; +import com.deem.zkui.utils.ZooKeeperUtil; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.apache.zookeeper.KeeperException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -25,26 +42,10 @@ import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Properties; -import javax.servlet.ServletException; -import javax.servlet.annotation.MultipartConfig; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.apache.zookeeper.KeeperException; -import com.deem.zkui.dao.Dao; -import com.deem.zkui.utils.ServletUtil; -import com.deem.zkui.utils.ZooKeeperUtil; -import java.util.Arrays; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @SuppressWarnings("serial") @WebServlet(urlPatterns = {"/import"}) diff --git a/src/main/java/com/deem/zkui/dao/Dao.java b/src/main/java/com/deem/zkui/dao/Dao.java index ddfa0c6..d4a1f9b 100644 --- a/src/main/java/com/deem/zkui/dao/Dao.java +++ b/src/main/java/com/deem/zkui/dao/Dao.java @@ -17,12 +17,18 @@ */ package com.deem.zkui.dao; +import com.deem.zkui.domain.Description; import com.deem.zkui.domain.History; +import com.deem.zkui.utils.ZooKeeperUtil; +import com.deem.zkui.vo.LeafBean; import com.googlecode.flyway.core.Flyway; import java.util.Arrays; import java.util.Date; +import java.util.Iterator; import java.util.List; import java.util.Properties; + +import com.mysql.jdbc.StringUtils; import org.javalite.activejdbc.Base; import org.slf4j.LoggerFactory; @@ -31,9 +37,16 @@ public class Dao { private final static Integer FETCH_LIMIT = 50; private final static org.slf4j.Logger logger = LoggerFactory.getLogger(Dao.class); private final Properties globalProps; + private int historyLimit = FETCH_LIMIT; public Dao(Properties globalProps) { this.globalProps = globalProps; + String historyLimit = this.globalProps == null ? null : this.globalProps.getProperty("historyLimit"); + if (StringUtils.isEmptyOrWhitespaceOnly(historyLimit)) { + try { + this.historyLimit = Integer.valueOf(historyLimit); + } catch (Exception e) {} + } } public void open() { @@ -61,20 +74,31 @@ public void checkNCreate() { } public List fetchHistoryRecords() { - this.open(); - List history = History.findAll().orderBy("ID desc").limit(FETCH_LIMIT); - history.size(); - this.close(); - return history; - + try { + this.open(); + List history = History.findAll().orderBy("ID desc").limit(historyLimit); + history.size(); + return history; + } catch (Exception ex) { + logger.error(Arrays.toString(ex.getStackTrace())); + } finally { + this.close(); + } + return null; } public List fetchHistoryRecordsByNode(String historyNode) { - this.open(); - List history = History.where("CHANGE_SUMMARY like ?", historyNode).orderBy("ID desc").limit(FETCH_LIMIT); - history.size(); - this.close(); - return history; + try { + this.open(); + List history = History.where("CHANGE_SUMMARY like ?", historyNode).orderBy("ID desc").limit(historyLimit); + history.size(); + return history; + } catch (Exception ex) { + logger.error(Arrays.toString(ex.getStackTrace())); + } finally { + this.close(); + } + return null; } public void insertHistory(String user, String ipAddress, String summary) { @@ -90,10 +114,88 @@ public void insertHistory(String user, String ipAddress, String summary) { history.setChangeSummary(summary); history.setChangeDate(new Date()); history.save(); + } catch (Exception ex) { + logger.error(Arrays.toString(ex.getStackTrace())); + } finally { this.close(); + } + } + + public void putDescription(List beans) { + if (beans == null || beans.size() < 1) return; + try { + StringBuffer buffer = new StringBuffer(); + for (LeafBean leaf : beans) { + buffer.append(((buffer.length() == 0 ? "" : "', '")) + ZooKeeperUtil.INSTANCE.pathFormat(leaf.getPath() + "/" + leaf.getName())); + } + this.open(); + List descriptions = Description.where("PATH IN ('" + buffer.toString() + "')"); + descriptions.size(); + Iterator iterator = descriptions.iterator(); + while (iterator.hasNext()) { + Description next = iterator.next(); + for (LeafBean leaf : beans) { + if (next.getPath().equals(ZooKeeperUtil.INSTANCE.pathFormat(leaf.getPath() + "/" + leaf.getName()))) { + leaf.setDescription(next.getDescription()); + break; + } + } + } } catch (Exception ex) { logger.error(Arrays.toString(ex.getStackTrace())); + } finally { + this.close(); + } + } + + public void deleteDescription(String... paths) { + try { + StringBuffer buffer = new StringBuffer(); + for (String path : paths) { + buffer.append((buffer.length() == 0 ? "" : " OR ") + "PATH = '" + ZooKeeperUtil.INSTANCE.pathFormat(path) + "'"); + } + this.open(); + Description.delete(buffer.toString()); + } catch (Exception ex) { + logger.error(Arrays.toString(ex.getStackTrace())); + } finally { + this.close(); + } + } + + public void deleteDescriptionByNode(String... nodes) { + try { + StringBuffer buffer = new StringBuffer(); + for (String node : nodes) { + buffer.append((buffer.length() == 0 ? "" : " OR ") + "PATH LIKE '" + ZooKeeperUtil.INSTANCE.pathFormat(node) + "%'"); + } + this.open(); + Description.delete(buffer.toString()); + } catch (Exception ex) { + logger.error(Arrays.toString(ex.getStackTrace())); + } finally { + this.close(); + } + } + + public void insertDescription(String path, String description) { + if (StringUtils.isEmptyOrWhitespaceOnly(path) || path.length() > 500) { + return; + } + try { + this.open(); + if (description.length() >= 500) { + description = description.substring(0, 500); + } + Description.delete("PATH = ?", ZooKeeperUtil.INSTANCE.pathFormat(path)); + Description desc = new Description(); + desc.setPath(ZooKeeperUtil.INSTANCE.pathFormat(path)); + desc.setDescription(description); + desc.save(); + } catch (Exception ex) { + logger.error(Arrays.toString(ex.getStackTrace())); + } finally { + this.close(); } - } } diff --git a/src/main/java/com/deem/zkui/domain/Description.java b/src/main/java/com/deem/zkui/domain/Description.java new file mode 100644 index 0000000..8c57c21 --- /dev/null +++ b/src/main/java/com/deem/zkui/domain/Description.java @@ -0,0 +1,56 @@ +/** + * + * Copyright (c) 2014, Deem Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + */ +package com.deem.zkui.domain; + +import org.javalite.activejdbc.Model; + +public class Description extends Model { + + private Long id; + private String path; + private String description; + + @Override + public Long getId() { + this.id = super.getLong("ID"); + return id; + } + + public void setId(Long id) { + super.setLong("ID", id); + } + + public String getPath() { + this.path = super.getString("PATH"); + return path; + } + + public void setPath(String path) { + super.setString("PATH", path); + } + + public String getDescription() { + this.description = super.getString("DESCRIPTION"); + return description; + } + + public void setDescription(String description) { + super.setString("DESCRIPTION", description); + } + +} diff --git a/src/main/java/com/deem/zkui/utils/ZooKeeperUtil.java b/src/main/java/com/deem/zkui/utils/ZooKeeperUtil.java index 3f2b530..d5eb572 100644 --- a/src/main/java/com/deem/zkui/utils/ZooKeeperUtil.java +++ b/src/main/java/com/deem/zkui/utils/ZooKeeperUtil.java @@ -19,14 +19,6 @@ import com.deem.zkui.vo.LeafBean; import com.deem.zkui.vo.ZKNode; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; @@ -42,6 +34,15 @@ import org.json.simple.parser.ParseException; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + public enum ZooKeeperUtil { INSTANCE; @@ -177,12 +178,12 @@ public void importData(List importFile, Boolean overwrite, ZooKeeper zk) int firstEq = line.indexOf('='); int secEq = line.indexOf('=', firstEq + 1); - String path = line.substring(0, firstEq); + String path = line.substring(0, firstEq).trim(); if ("/".equals(path)) { path = ""; } - String name = line.substring(firstEq + 1, secEq); - String value = readExternalizedNodeValue(line.substring(secEq + 1)); + String name = line.substring(firstEq + 1, secEq).trim(); + String value = readExternalizedNodeValue(line.substring(secEq + 1)).trim(); String fullNodePath = path + "/" + name; // Skip import of system node @@ -234,6 +235,7 @@ private void createPathAndNode(String path, String name, byte[] data, boolean fo private void createIfDoesntExist(String path, byte[] data, boolean force, ZooKeeper zooKeeper) throws InterruptedException, KeeperException { try { + path = pathFormat(path); zooKeeper.create(path, data, defaultAcl(), CreateMode.PERSISTENT); } catch (KeeperException ke) { //Explicit Overwrite @@ -342,7 +344,7 @@ public LeafBean getNodeValue(ZooKeeper zk, String path, String childPath, String //Reason exception is caught here is so that lookup can continue to happen if a particular property is not found at parent level. try { logger.trace("Lookup: path=" + path + ",childPath=" + childPath + ",child=" + child + ",authRole=" + authRole); - byte[] dataBytes = zk.getData(childPath, false, new Stat()); + byte[] dataBytes = zk.getData(pathFormat(childPath), false, new Stat()); if (!authRole.equals(ROLE_ADMIN)) { if (checkIfPwdField(child)) { return (new LeafBean(path, child, SOPA_PIPA.getBytes())); @@ -360,11 +362,7 @@ public LeafBean getNodeValue(ZooKeeper zk, String path, String childPath, String } public Boolean checkIfPwdField(String property) { - if (property.contains("PWD") || property.contains("pwd") || property.contains("PASSWORD") || property.contains("password") || property.contains("PASSWD") || property.contains("passwd")) { - return true; - } else { - return false; - } + return property.contains("PWD") || property.contains("pwd") || property.contains("PASSWORD") || property.contains("password") || property.contains("PASSWD") || property.contains("passwd"); } public void createNode(String path, String name, String value, ZooKeeper zk) throws KeeperException, InterruptedException { @@ -383,10 +381,13 @@ public void createFolder(String folderPath, String propertyName, String property } public void setPropertyValue(String path, String name, String value, ZooKeeper zk) throws KeeperException, InterruptedException { - String nodePath = path + name; + String nodePath = pathFormat(path + name); logger.debug("Setting property " + nodePath + " to " + value); - zk.setData(nodePath, value.getBytes(), -1); - + if (!nodeExists(nodePath, zk)) { + createPathAndNode(path, name, value.getBytes(), true, zk); + } else { + zk.setData(nodePath, value.getBytes(), -1); + } } public boolean nodeExists(String nodeFullPath, ZooKeeper zk) throws KeeperException, InterruptedException { @@ -431,4 +432,9 @@ public void closeZooKeeper(ZooKeeper zk) throws InterruptedException { } } + + public String pathFormat(String path) { + return null != path && !"".equals(path) ? path.replaceAll("\\\\+", "\\\\").replaceAll("[\\/]+", "/") : path; + } + } diff --git a/src/main/java/com/deem/zkui/vo/LeafBean.java b/src/main/java/com/deem/zkui/vo/LeafBean.java index 9b747b8..35b5e0f 100644 --- a/src/main/java/com/deem/zkui/vo/LeafBean.java +++ b/src/main/java/com/deem/zkui/vo/LeafBean.java @@ -17,10 +17,14 @@ */ package com.deem.zkui.vo; + import java.nio.charset.StandardCharsets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; + public class LeafBean implements Comparable { private final static Logger logger = LoggerFactory.getLogger(LeafBean.class); @@ -28,6 +32,7 @@ public class LeafBean implements Comparable { private String name; private byte[] value; private String strValue; + private String description; public LeafBean(String path, String name, byte[] value) { super(); @@ -68,6 +73,14 @@ public void setStrValue(String strValue) { this.strValue = strValue; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + @Override public int compareTo(LeafBean o) { return (this.path + this.name).compareTo((o.path + o.name)); diff --git a/src/main/resources/db/migration/V1__Core.sql b/src/main/resources/db/migration/V1__Core.sql index fadfd7e..0e3c8ea 100644 --- a/src/main/resources/db/migration/V1__Core.sql +++ b/src/main/resources/db/migration/V1__Core.sql @@ -21,4 +21,10 @@ change_user varchar(100), change_date datetime, change_summary varchar(500), change_ip varchar(500) +); + +create table descriptions ( +id bigint default null auto_increment primary key, +path varchar(500), +description varchar(500) ); \ No newline at end of file diff --git a/src/main/resources/webapp/css/zkacd.css b/src/main/resources/webapp/css/zkacd.css index 6175ee3..b4de941 100644 --- a/src/main/resources/webapp/css/zkacd.css +++ b/src/main/resources/webapp/css/zkacd.css @@ -27,6 +27,11 @@ body { max-height: 620px; } +.scroll-pane .tree-node{ + height: 20px; + white-space: nowrap; +} + .wrap-table{ table-layout: fixed; word-wrap: break-word; diff --git a/src/main/resources/webapp/js/home.js b/src/main/resources/webapp/js/home.js index ad6443b..2ce3754 100644 --- a/src/main/resources/webapp/js/home.js +++ b/src/main/resources/webapp/js/home.js @@ -29,6 +29,8 @@ $(document).ready(function() { $(".href-select").click(function() { var propName = $(this).text(); var propVal = $(this).attr('itemprop'); + var propDesc = $(this).attr('itemdesc'); + $("#newDescription").val(propDesc); $("#newProperty").attr('readonly', true); $("#newProperty").val(propName); $("#newValue").val(propVal); @@ -44,5 +46,11 @@ $(document).ready(function() { $("#savePropertyBtn").show(); }); + //trim input or textarea + $('.container form').submit(function(){ + $(this).find('input:text,textarea').each(function(){ + $(this).val($.trim($(this).val())); + }); + }); }); \ No newline at end of file diff --git a/src/main/resources/webapp/template/history.ftl.html b/src/main/resources/webapp/template/history.ftl.html index fea069c..d588651 100644 --- a/src/main/resources/webapp/template/history.ftl.html +++ b/src/main/resources/webapp/template/history.ftl.html @@ -22,7 +22,7 @@
-

Top 50 Changes

+

Top ${historyLimit!50} Changes

diff --git a/src/main/resources/webapp/template/home.ftl.html b/src/main/resources/webapp/template/home.ftl.html index 5af7fbe..f0e8997 100644 --- a/src/main/resources/webapp/template/home.ftl.html +++ b/src/main/resources/webapp/template/home.ftl.html @@ -54,10 +54,11 @@ <..>
<#list nodeLst as node> - - - ${node} -
+
+ + + ${node} +
@@ -68,6 +69,9 @@ Name Value + <#if showDesc> + Description + @@ -78,13 +82,9 @@ <#if authRole?? && authRole == 'ADMIN' > - <#if leaf.value??> - ${leaf.name} - <#else> - ${leaf.name} - + ${leaf.name} <#else> - ${leaf.name} + ${leaf.name} <#if navigate?? && navigate == 'true'> @@ -96,7 +96,9 @@ - + <#if showDesc> + ${leaf.description!} + @@ -175,6 +177,13 @@ Value + <#if showDesc> +
+
+ Description + +
+