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
This commit is contained in:
Erick Erickson 2012-02-24 00:28:19 +00:00
parent 48bb56dea8
commit b32b52578d
1 changed files with 235 additions and 336 deletions

View File

@ -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("<a href=\"zookeeper?");
if (path != null) {
out.print("path=");
out.print(URLEncoder.encode(path, "UTF-8"));
}
if (detail) {
out.print("&detail=" + detail);
}
if (fullpath != FULLPATH_DEFAULT) {
out.print("&fullpath=" + fullpath);
}
if (addr != null) {
out.print("&addr=");
out.print(URLEncoder.encode(addr, "UTF-8"));
}
void writeError(int code, String msg) {
response.setStatus(code);
out.print("\">");
xmlescape(label);
out.print("</a>");
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<String> 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<String> 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;
}
}
}