From b32b52578d207763e965e850bc72c86d647bca08 Mon Sep 17 00:00:00 2001 From: Erick Erickson Date: Fri, 24 Feb 2012 00:28:19 +0000 Subject: [PATCH] Patch for SOLR-3155. Fixes JSON output format error and alphabetizes children of nodex. git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1293039 13f79535-47bb-0310-9956-ffa450edef68 --- .../solr/servlet/ZookeeperInfoServlet.java | 571 +++++++----------- 1 file changed, 235 insertions(+), 336 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/servlet/ZookeeperInfoServlet.java b/solr/core/src/java/org/apache/solr/servlet/ZookeeperInfoServlet.java index c736580d852..008ec76727e 100644 --- a/solr/core/src/java/org/apache/solr/servlet/ZookeeperInfoServlet.java +++ b/solr/core/src/java/org/apache/solr/servlet/ZookeeperInfoServlet.java @@ -17,6 +17,21 @@ package org.apache.solr.servlet; +import org.apache.noggit.CharArr; +import org.apache.noggit.JSONWriter; +import org.apache.solr.cloud.ZkController; +import org.apache.solr.common.cloud.SolrZkClient; +import org.apache.solr.common.util.StrUtils; +import org.apache.solr.core.CoreContainer; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.data.Stat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; @@ -25,19 +40,6 @@ import java.util.Date; import java.util.List; import java.util.concurrent.TimeoutException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.solr.cloud.ZkController; -import org.apache.solr.common.cloud.SolrZkClient; -import org.apache.solr.common.util.StrUtils; -import org.apache.solr.common.util.XML; -import org.apache.solr.core.CoreContainer; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.data.Stat; - /** * Zookeeper Info @@ -45,6 +47,7 @@ import org.apache.zookeeper.data.Stat; * @since solr 4.0 */ public final class ZookeeperInfoServlet extends HttpServlet { + static final Logger log = LoggerFactory.getLogger(ZookeeperInfoServlet.class); @Override public void init() throws ServletException { @@ -53,27 +56,28 @@ public final class ZookeeperInfoServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { + throws IOException, ServletException { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); + // This attribute is set by the SolrDispatchFilter CoreContainer cores = (CoreContainer) request.getAttribute("org.apache.solr.CoreContainer"); - + String path = request.getParameter("path"); String addr = request.getParameter("addr"); - - if (addr != null && addr.length() == 0) - { + + if (addr != null && addr.length() == 0) { addr = null; } - + String detailS = request.getParameter("detail"); boolean detail = detailS != null && detailS.equals("true"); PrintWriter out = response.getWriter(); - + + ZKPrinter printer = new ZKPrinter(response, out, cores.getZkController(), addr); printer.detail = detail; - + try { printer.print(path); } finally { @@ -84,455 +88,350 @@ public final class ZookeeperInfoServlet extends HttpServlet { @Override public void doPost(HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - doGet(request,response); + throws IOException, ServletException { + doGet(request, response); } //-------------------------------------------------------------------------------------- - // + // //-------------------------------------------------------------------------------------- - - static class ZKPrinter - { - + + static class ZKPrinter { static boolean FULLPATH_DEFAULT = false; - + boolean indent = true; boolean fullpath = FULLPATH_DEFAULT; boolean detail = false; - + String addr; // the address passed to us String keeperAddr; // the address we're connected to - + boolean doClose; // close the client after done if we opened it - - HttpServletResponse response; - PrintWriter out; + + final HttpServletResponse response; + final PrintWriter out; SolrZkClient zkClient; - + int level; int maxData = 95; - - public ZKPrinter(HttpServletResponse response, PrintWriter out, ZkController controller, String addr) throws IOException - { + + public ZKPrinter(HttpServletResponse response, PrintWriter out, ZkController controller, String addr) throws IOException { this.response = response; this.out = out; this.addr = addr; - - if (addr == null) - { - if (controller != null) - { + + if (addr == null) { + if (controller != null) { // this core is zk enabled keeperAddr = controller.getZkServerAddress(); zkClient = controller.getZkClient(); - if (zkClient != null && zkClient.isConnected()) - { + if (zkClient != null && zkClient.isConnected()) { return; - } - else - { + } else { // try a different client with this address addr = keeperAddr; } } } - + keeperAddr = addr; - if (addr == null) - { - response.setStatus(404); - out.println - ( - "{" + - "\"status\": 404" + - ", \"error\" : \"Zookeeper is not configured for this Solr Core. Please try connecting to an alternate zookeeper address.\"" + - "}" - ); + if (addr == null) { + writeError(404, "Zookeeper is not configured for this Solr Core. Please try connecting to an alternate zookeeper address."); return; } - - try - { + + try { zkClient = new SolrZkClient(addr, 10000); doClose = true; - } - catch (TimeoutException e) - { - response.setStatus(503); - out.println - ( - "{" + - "\"status\": 503" + - ", \"error\" : \"Could not connect to zookeeper at '" + addr + "'\"" + - "}" - ); + } catch (TimeoutException e) { + writeError(503, "Could not connect to zookeeper at '" + addr + "'\""); zkClient = null; return; - } - catch (InterruptedException e) - { + } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); - response.setStatus(503); - out.println - ( - "{" + - "\"status\": 503" + - ", \"error\" : \"Could not connect to zookeeper at '" + addr + "'\"" + - "}" - ); + writeError(503, "Could not connect to zookeeper at '" + addr + "'\""); zkClient = null; return; } - + } - public void close() - { + public void close() { try { - if (doClose) - { + if (doClose) { zkClient.close(); } } catch (InterruptedException e) { - // ignore exception on close + // ignore exception on close } } - + // main entry point - void print(String path) throws IOException - { + void print(String path) throws IOException { if (zkClient == null) { return; } - + // normalize path if (path == null) { path = "/"; - } - else { + } else { path.trim(); - if (path.length() == 0) - { + if (path.length() == 0) { path = "/"; } } - - if (path.endsWith("/") && path.length() > 1) - { + + if (path.endsWith("/") && path.length() > 1) { path = path.substring(0, path.length() - 1); } - + int idx = path.lastIndexOf('/'); String parent = idx >= 0 ? path.substring(0, idx) : path; - if (parent.length() == 0) - { + if (parent.length() == 0) { parent = "/"; } - - out.println("{"); - - if (detail) - { - printZnode(path); - out.println(", "); - } - - out.println("\"tree\" : ["); - printTree(path); - out.println("]"); - - out.println("}"); - } - - void exception(Exception e) - { - response.setStatus(500); - out.println - ( - "{" + - "\"status\": 500" + - ", \"error\" : \"" + e.toString() + "\"" + - "}" - ); - } - - void xmlescape(String s) - { - try - { - XML.escapeCharData(s, out); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - } - - // collapse all whitespace to a single space or escaped newline - String compress(String str) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); - boolean whitespace = false; - boolean newline = false; - while (Character.isWhitespace(ch)) { - whitespace = true; - if (ch == '\n') - newline = true; - if (++i >= str.length()) - return sb.toString(); - ch = str.charAt(i); + + CharArr chars = new CharArr(); + JSONWriter json = new JSONWriter(chars, 2); + json.startObject(); + + if (detail) { + if (!printZnode(json, path)) { + return; } - - if (newline) { - // sb.append("\\n"); - sb.append(" "); // collapse newline to two spaces - } else if (whitespace) { - sb.append(' '); - } - - // TODO: handle non-printable chars - sb.append(ch); - - if (sb.length() >= maxData) - return sb.toString() + " ..."; + json.writeValueSeparator(); } - return sb.toString(); + + json.writeString("tree"); + json.writeNameSeparator(); + json.startArray(); + if (!printTree(json, path)) { + return; // there was an error + } + json.endArray(); + json.endObject(); + out.println(chars.toString()); } - void url(String label, String path, boolean detail) throws IOException { - try { - out.print(""); - xmlescape(label); - out.print(""); + CharArr chars = new CharArr(); + JSONWriter w = new JSONWriter(chars, 2); + w.startObject(); + w.indent(); + w.writeString("status"); + w.writeNameSeparator(); + w.write(code); + w.writeValueSeparator(); + w.indent(); + w.writeString("error"); + w.writeNameSeparator(); + w.writeString(msg); + w.endObject(); - } catch (UnsupportedEncodingException e) { - exception(e); - } + out.println(chars.toString()); } - void printTree(String path) throws IOException - { + + boolean printTree(JSONWriter json, String path) throws IOException { String label = path; - if (!fullpath) - { + if (!fullpath) { int idx = path.lastIndexOf('/'); label = idx > 0 ? path.substring(idx + 1) : path; } - - //url(label, path, true); - out.println("{"); - out.println("\"data\" : \"" + label + "\""); - + json.startObject(); + //writeKeyValue(json, "data", label, true ); + json.writeString("data"); + json.writeNameSeparator(); + + json.startObject(); + writeKeyValue(json, "title", label, true); + json.writeValueSeparator(); + json.writeString("attr"); + json.writeNameSeparator(); + json.startObject(); + writeKeyValue(json, "href", "zookeeper?detail=true&path=" + URLEncoder.encode(path, "UTF-8"), true); + json.endObject(); + json.endObject(); + Stat stat = new Stat(); - try - { + try { byte[] data = zkClient.getData(path, null, stat, true); - - if( stat.getEphemeralOwner() != 0 ) - { - out.println(", \"ephemeral\" : true"); - out.println(", \"version\" : \"" + stat.getVersion() + "\""); + + if (stat.getEphemeralOwner() != 0) { + writeKeyValue(json, "ephemeral", true, false); + writeKeyValue(json, "version", stat.getVersion(), false); } - + /* if (stat.getNumChildren() != 0) { + writeKeyValue(json, "children_count", stat.getNumChildren(), false ); out.println(", \"children_count\" : \"" + stat.getNumChildren() + "\""); } */ - + //if (data != null) - if( stat.getDataLength() != 0 ) - { + if (stat.getDataLength() != 0) { String str; - try - { + try { str = new String(data, "UTF-8"); str = str.replaceAll("\\\"", "\\\\\""); - - out.print(", \"content\" : \""); - //xmlescape(compress(str)); - out.print(compress(str)); - out.println("\""); - } - catch (UnsupportedEncodingException e) - { + + //writeKeyValue(json, "content", str, false ); + } catch (UnsupportedEncodingException e) { // not UTF8 StringBuilder sb = new StringBuilder("BIN("); sb.append("len=" + data.length); sb.append("hex="); int limit = Math.min(data.length, maxData / 2); - for (int i = 0; i < limit; i++) - { + for (int i = 0; i < limit; i++) { byte b = data[i]; sb.append(StrUtils.HEX_DIGITS[(b >> 4) & 0xf]); sb.append(StrUtils.HEX_DIGITS[b & 0xf]); } - if (limit != data.length) - { + if (limit != data.length) { sb.append("..."); } sb.append(")"); str = sb.toString(); - //out.print(str); + //?? writeKeyValue(json, "content", str, false ); } } - } - catch (IllegalArgumentException e) - { + } catch (IllegalArgumentException e) { // path doesn't exist (must have been removed) - out.println("(path gone)"); + writeKeyValue(json, "warning", "(path gone)", false); + } catch (KeeperException e) { + writeKeyValue(json, "warning", e.toString(), false); + log.warn("Keeper Exception", e); + } catch (InterruptedException e) { + writeKeyValue(json, "warning", e.toString(), false); + log.warn("InterruptedException", e); } - catch (KeeperException e) - { - e.printStackTrace(); - } - catch (InterruptedException e) - { - e.printStackTrace(); - } - - if( stat.getNumChildren() > 0 ) - { - out.print(", \"children\" : ["); - - List children = null; - try - { - children = zkClient.getChildren(path, null, true); + + if (stat.getNumChildren() > 0) { + json.writeValueSeparator(); + if (indent) { + json.indent(); } - catch (KeeperException e) - { - exception(e); - return; - } - catch (InterruptedException e) - { - exception(e); - } - catch (IllegalArgumentException e) - { - // path doesn't exist (must have been removed) - out.println("(children gone)"); - } - - Integer i = 0; - for( String child : children ) - { - if( 0 != i ) - { - out.print(", "); + json.writeString("children"); + json.writeNameSeparator(); + json.startArray(); + + try { + List children = zkClient.getChildren(path, null, true); + java.util.Collections.sort(children); + + boolean first = true; + for (String child : children) { + if (!first) { + json.writeValueSeparator(); + } + + String childPath = path + (path.endsWith("/") ? "" : "/") + child; + if (!printTree(json, childPath)) { + return false; + } + first = false; } - - String childPath = path + (path.endsWith("/") ? "" : "/") + child; - printTree( childPath ); - - i++; + } catch (KeeperException e) { + writeError(500, e.toString()); + return false; + } catch (InterruptedException e) { + writeError(500, e.toString()); + return false; + } catch (IllegalArgumentException e) { + // path doesn't exist (must have been removed) + json.writeString("(children gone)"); } - - out.println("]"); + + json.endArray(); } - - out.println("}"); + + json.endObject(); + return true; } String time(long ms) { return (new Date(ms)).toString() + " (" + ms + ")"; } - - void printZnode(String path) throws IOException - { - try - { + + public void writeKeyValue(JSONWriter json, String k, Object v, boolean isFirst) { + if (!isFirst) { + json.writeValueSeparator(); + } + if (indent) { + json.indent(); + } + json.writeString(k); + json.writeNameSeparator(); + json.write(v); + } + + boolean printZnode(JSONWriter json, String path) throws IOException { + try { Stat stat = new Stat(); byte[] data = zkClient.getData(path, null, stat, true); - - out.println("\"znode\" : {"); - - out.print("\"path\" : \""); - xmlescape(path); - out.println("\""); - - out.println(", \"version\" : \"" + stat.getVersion() + "\""); - out.println(", \"aversion\" : \"" + stat.getAversion() + "\""); - out.println(", \"cversion\" : \"" + stat.getCversion() + "\""); - out.println(", \"ctime\" : \"" + time(stat.getCtime()) + "\""); - out.println(", \"mtime\" : \"" + time(stat.getMtime()) + "\""); - out.println(", \"czxid\" : \"" + stat.getCzxid() + "\""); - out.println(", \"mzxid\" : \"" + stat.getMzxid() + "\""); - out.println(", \"pzxid\" : \"" + stat.getPzxid() + "\""); - out.println(", \"children_count\" : \"" + stat.getNumChildren() + "\""); - out.println(", \"ephemeralOwner\" : \"" + stat.getEphemeralOwner() + "\""); - out.println(", \"dataLength\" : \"" + stat.getDataLength() + "\""); - - if( stat.getDataLength() != 0 ) - { - boolean isBinary = false; + + json.writeString("znode"); + json.writeNameSeparator(); + json.startObject(); + + writeKeyValue(json, "path", path, true); + + json.writeValueSeparator(); + json.writeString("prop"); + json.writeNameSeparator(); + json.startObject(); + writeKeyValue(json, "version", stat.getVersion(), true); + writeKeyValue(json, "aversion", stat.getAversion(), false); + writeKeyValue(json, "children_count", stat.getNumChildren(), false); + writeKeyValue(json, "ctime", time(stat.getCtime()), false); + writeKeyValue(json, "cversion", stat.getCversion(), false); + writeKeyValue(json, "czxid", stat.getCzxid(), false); + writeKeyValue(json, "dataLength", stat.getDataLength(), false); + writeKeyValue(json, "ephemeralOwner", stat.getEphemeralOwner(), false); + writeKeyValue(json, "mtime", time(stat.getMtime()), false); + writeKeyValue(json, "mzxid", stat.getMzxid(), false); + writeKeyValue(json, "pzxid", stat.getPzxid(), false); + json.endObject(); + + if (stat.getDataLength() != 0) { String str; - try - { + try { str = new String(data, "UTF-8"); - } - catch (UnsupportedEncodingException e) - { + } catch (UnsupportedEncodingException e) { // The results are unspecified // when the bytes are not properly encoded. - + // not UTF8 StringBuilder sb = new StringBuilder(data.length * 2); - for (int i = 0; i < data.length; i++) - { + for (int i = 0; i < data.length; i++) { byte b = data[i]; sb.append(StrUtils.HEX_DIGITS[(b >> 4) & 0xf]); sb.append(StrUtils.HEX_DIGITS[b & 0xf]); - if ((i & 0x3f) == 0x3f) - { + if ((i & 0x3f) == 0x3f) { sb.append("\n"); } } str = sb.toString(); } str = str.replaceAll("\\\"", "\\\\\""); - - out.print(", \"data\" : \""); - //xmlescape(str); - out.print(str); - out.println("\""); + writeKeyValue(json, "data", str, false); } - - out.println("}"); - - } - catch (KeeperException e) - { - exception(e); - return; - } - catch (InterruptedException e) - { - exception(e); + json.endObject(); + } catch (KeeperException e) { + writeError(500, e.toString()); + return false; + } catch (InterruptedException e) { + writeError(500, e.toString()); + return false; } + return true; } } } -