SOLR-8083: convert the ZookeeperInfoServlet to a request handler at /admin/zookeeper

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1705662 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Noble Paul 2015-09-28 12:10:39 +00:00
parent ab3ff9f0fe
commit a72c536a2a
7 changed files with 202 additions and 186 deletions

View File

@ -49,6 +49,7 @@ import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.handler.admin.CoreAdminHandler; import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.handler.admin.InfoHandler; import org.apache.solr.handler.admin.InfoHandler;
import org.apache.solr.handler.admin.SecurityConfHandler; import org.apache.solr.handler.admin.SecurityConfHandler;
import org.apache.solr.handler.admin.ZookeeperInfoHandler;
import org.apache.solr.handler.component.HttpShardHandlerFactory; import org.apache.solr.handler.component.HttpShardHandlerFactory;
import org.apache.solr.handler.component.ShardHandlerFactory; import org.apache.solr.handler.component.ShardHandlerFactory;
import org.apache.solr.logging.LogWatcher; import org.apache.solr.logging.LogWatcher;
@ -398,6 +399,7 @@ public class CoreContainer {
initializeAuthorizationPlugin((Map<String, Object>) securityConfig.data.get("authorization")); initializeAuthorizationPlugin((Map<String, Object>) securityConfig.data.get("authorization"));
initializeAuthenticationPlugin((Map<String, Object>) securityConfig.data.get("authentication")); initializeAuthenticationPlugin((Map<String, Object>) securityConfig.data.get("authentication"));
containerHandlers.put(ZK_PATH, new ZookeeperInfoHandler(this));
securityConfHandler = new SecurityConfHandler(this); securityConfHandler = new SecurityConfHandler(this);
collectionsHandler = createHandler(cfg.getCollectionsHandlerClass(), CollectionsHandler.class); collectionsHandler = createHandler(cfg.getCollectionsHandlerClass(), CollectionsHandler.class);
containerHandlers.put(COLLECTIONS_HANDLER_PATH, collectionsHandler); containerHandlers.put(COLLECTIONS_HANDLER_PATH, collectionsHandler);

View File

@ -15,13 +15,12 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.solr.servlet; package org.apache.solr.handler.admin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -29,6 +28,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -46,21 +46,33 @@ import org.apache.solr.common.cloud.OnReconnect;
import org.apache.solr.common.cloud.Replica; import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.SolrZkClient; import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.Utils; import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer; import org.apache.solr.core.CoreContainer;
import org.apache.solr.util.FastWriter; import org.apache.solr.handler.ReplicationHandler;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.JSONResponseWriter;
import org.apache.solr.response.RawResponseWriter;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.util.SimplePostTool.BAOS;
import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.server.ByteBufferInputStream;
import org.noggit.CharArr; import org.noggit.CharArr;
import org.noggit.JSONWriter; import org.noggit.JSONWriter;
import org.noggit.ObjectBuilder; import org.noggit.ObjectBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CommonParams.OMIT_HEADER;
import static org.apache.solr.common.params.CommonParams.PATH; import static org.apache.solr.common.params.CommonParams.PATH;
import static org.apache.solr.common.params.CommonParams.WT;
/** /**
@ -68,20 +80,32 @@ import static org.apache.solr.common.params.CommonParams.PATH;
* *
* @since solr 4.0 * @since solr 4.0
*/ */
public final class ZookeeperInfoServlet extends BaseSolrServlet { public final class ZookeeperInfoHandler extends RequestHandlerBase {
static final Logger log = LoggerFactory.getLogger(ZookeeperInfoServlet.class); private final CoreContainer cores;
static final Logger log = LoggerFactory.getLogger(ZookeeperInfoHandler.class);
// used for custom sorting collection names looking like prefix## // used for custom sorting collection names looking like prefix##
// only go out to 7 digits (which safely fits in an int) // only go out to 7 digits (which safely fits in an int)
private static final Pattern endsWithDigits = Pattern.compile("^(\\D*)(\\d{1,7}?)$"); private static final Pattern endsWithDigits = Pattern.compile("^(\\D*)(\\d{1,7}?)$");
public ZookeeperInfoHandler(CoreContainer cc) {
this.cores = cc;
}
@Override
public String getDescription() {
return "Fetch Zookeeper contents";
}
/** /**
* Enumeration of ways to filter collections on the graph panel. * Enumeration of ways to filter collections on the graph panel.
*/ */
static enum FilterType { static enum FilterType {
none, name, status none, name, status
} }
/** /**
* Holds state of a single page of collections requested from the cloud panel. * Holds state of a single page of collections requested from the cloud panel.
*/ */
@ -92,84 +116,84 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
int rows = -1; int rows = -1;
FilterType filterType; FilterType filterType;
String filter; String filter;
PageOfCollections(int start, int rows, FilterType filterType, String filter) { PageOfCollections(int start, int rows, FilterType filterType, String filter) {
this.start = start; this.start = start;
this.rows = rows; this.rows = rows;
this.filterType = filterType; this.filterType = filterType;
this.filter = filter; this.filter = filter;
} }
void selectPage(List<String> collections) { void selectPage(List<String> collections) {
numFound = collections.size(); numFound = collections.size();
// start with full set and then find the sublist for the desired selected // start with full set and then find the sublist for the desired selected
selected = collections; selected = collections;
if (rows > 0) { // paging desired if (rows > 0) { // paging desired
if (start > numFound) if (start > numFound)
start = 0; // this might happen if they applied a new filter start = 0; // this might happen if they applied a new filter
int lastIndex = Math.min(start+rows, numFound); int lastIndex = Math.min(start + rows, numFound);
if (start > 0 || lastIndex < numFound) if (start > 0 || lastIndex < numFound)
selected = collections.subList(start, lastIndex); selected = collections.subList(start, lastIndex);
} }
} }
/** /**
* Filters a list of collections by name if applicable. * Filters a list of collections by name if applicable.
*/ */
List<String> applyNameFilter(List<String> collections) { List<String> applyNameFilter(List<String> collections) {
if (filterType != FilterType.name || filter == null) if (filterType != FilterType.name || filter == null)
return collections; // name filter doesn't apply return collections; // name filter doesn't apply
// typically, a user will type a prefix and then *, e.g. tj* // typically, a user will type a prefix and then *, e.g. tj*
// when they really mean tj.* // when they really mean tj.*
String regexFilter = (!filter.endsWith(".*") && filter.endsWith("*")) String regexFilter = (!filter.endsWith(".*") && filter.endsWith("*"))
? filter.substring(0,filter.length()-1)+".*" : filter; ? filter.substring(0, filter.length() - 1) + ".*" : filter;
// case-insensitive // case-insensitive
if (!regexFilter.startsWith("(?i)")) if (!regexFilter.startsWith("(?i)"))
regexFilter = "(?i)"+regexFilter; regexFilter = "(?i)" + regexFilter;
Pattern filterRegex = Pattern.compile(regexFilter); Pattern filterRegex = Pattern.compile(regexFilter);
List<String> filtered = new ArrayList<String>(); List<String> filtered = new ArrayList<String>();
for (String next : collections) { for (String next : collections) {
if (matches(filterRegex, next)) if (matches(filterRegex, next))
filtered.add(next); filtered.add(next);
} }
return filtered; return filtered;
} }
/** /**
* Walk the collection state JSON object to see if it has any replicas that match * Walk the collection state JSON object to see if it has any replicas that match
* the state the user is filtering by. * the state the user is filtering by.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final boolean matchesStatusFilter(Map<String,Object> collectionState, Set<String> liveNodes) { final boolean matchesStatusFilter(Map<String, Object> collectionState, Set<String> liveNodes) {
if (filterType != FilterType.status || filter == null || filter.length() == 0) if (filterType != FilterType.status || filter == null || filter.length() == 0)
return true; // no status filter, so all match return true; // no status filter, so all match
boolean isHealthy = true; // means all replicas for all shards active boolean isHealthy = true; // means all replicas for all shards active
boolean hasDownedShard = false; // means one or more shards is down boolean hasDownedShard = false; // means one or more shards is down
boolean replicaInRecovery = false; boolean replicaInRecovery = false;
Map<String,Object> shards = (Map<String,Object>)collectionState.get("shards"); Map<String, Object> shards = (Map<String, Object>) collectionState.get("shards");
for (String shardId : shards.keySet()) { for (String shardId : shards.keySet()) {
boolean hasActive = false; boolean hasActive = false;
Map<String,Object> shard = (Map<String,Object>)shards.get(shardId); Map<String, Object> shard = (Map<String, Object>) shards.get(shardId);
Map<String,Object> replicas = (Map<String,Object>)shard.get("replicas"); Map<String, Object> replicas = (Map<String, Object>) shard.get("replicas");
for (String replicaId : replicas.keySet()) { for (String replicaId : replicas.keySet()) {
Map<String,Object> replicaState = (Map<String,Object>)replicas.get(replicaId); Map<String, Object> replicaState = (Map<String, Object>) replicas.get(replicaId);
Replica.State coreState = Replica.State.getState((String)replicaState.get(ZkStateReader.STATE_PROP)); Replica.State coreState = Replica.State.getState((String) replicaState.get(ZkStateReader.STATE_PROP));
String nodeName = (String)replicaState.get("node_name"); String nodeName = (String) replicaState.get("node_name");
// state can lie to you if the node is offline, so need to reconcile with live_nodes too // state can lie to you if the node is offline, so need to reconcile with live_nodes too
if (!liveNodes.contains(nodeName)) if (!liveNodes.contains(nodeName))
coreState = Replica.State.DOWN; // not on a live node, so must be down coreState = Replica.State.DOWN; // not on a live node, so must be down
if (coreState == Replica.State.ACTIVE) { if (coreState == Replica.State.ACTIVE) {
hasActive = true; // assumed no replicas active and found one that is for this shard hasActive = true; // assumed no replicas active and found one that is for this shard
} else { } else {
@ -177,13 +201,13 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
replicaInRecovery = true; replicaInRecovery = true;
} }
isHealthy = false; // assumed healthy and found one replica that is not isHealthy = false; // assumed healthy and found one replica that is not
} }
} }
if (!hasActive) if (!hasActive)
hasDownedShard = true; // this is bad hasDownedShard = true; // this is bad
} }
if ("healthy".equals(filter)) { if ("healthy".equals(filter)) {
return isHealthy; return isHealthy;
} else if ("degraded".equals(filter)) { } else if ("degraded".equals(filter)) {
@ -193,24 +217,24 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
} else if (Replica.State.getState(filter) == Replica.State.RECOVERING) { } else if (Replica.State.getState(filter) == Replica.State.RECOVERING) {
return !isHealthy && replicaInRecovery; return !isHealthy && replicaInRecovery;
} }
return true; return true;
} }
final boolean matches(final Pattern filter, final String collName) { final boolean matches(final Pattern filter, final String collName) {
return filter.matcher(collName).matches(); return filter.matcher(collName).matches();
} }
String getPagingHeader() { String getPagingHeader() {
return start+"|"+rows+"|"+numFound+"|"+(filterType != null ? filterType.toString() : "")+"|"+(filter != null ? filter : ""); return start + "|" + rows + "|" + numFound + "|" + (filterType != null ? filterType.toString() : "") + "|" + (filter != null ? filter : "");
} }
public String toString() { public String toString() {
return getPagingHeader(); return getPagingHeader();
} }
} }
/** /**
* Supports paged navigation of collections on the cloud panel. To avoid serving * Supports paged navigation of collections on the cloud panel. To avoid serving
* stale collection data, this object watches the /collections znode, which will * stale collection data, this object watches the /collections znode, which will
@ -230,42 +254,42 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
cachedCollections = null; cachedCollections = null;
} }
} }
/** /**
* Create a merged view of all collections (internal from /clusterstate.json and external from /collections/?/state.json * Create a merged view of all collections (internal from /clusterstate.json and external from /collections/?/state.json
*/ */
private synchronized List<String> getCollections(SolrZkClient zkClient) throws KeeperException, InterruptedException { private synchronized List<String> getCollections(SolrZkClient zkClient) throws KeeperException, InterruptedException {
if (cachedCollections == null) { if (cachedCollections == null) {
// cache is stale, rebuild the full list ... // cache is stale, rebuild the full list ...
cachedCollections = new ArrayList<String>(); cachedCollections = new ArrayList<String>();
List<String> fromZk = zkClient.getChildren("/collections", this, true); List<String> fromZk = zkClient.getChildren("/collections", this, true);
if (fromZk != null) if (fromZk != null)
cachedCollections.addAll(fromZk); cachedCollections.addAll(fromZk);
// sort the final merged set of collections // sort the final merged set of collections
Collections.sort(cachedCollections, this); Collections.sort(cachedCollections, this);
} }
return cachedCollections; return cachedCollections;
} }
/** /**
* Gets the requested page of collections after applying filters and offsets. * Gets the requested page of collections after applying filters and offsets.
*/ */
public PageOfCollections fetchPage(PageOfCollections page, SolrZkClient zkClient) public PageOfCollections fetchPage(PageOfCollections page, SolrZkClient zkClient)
throws KeeperException, InterruptedException { throws KeeperException, InterruptedException {
List<String> children = getCollections(zkClient); List<String> children = getCollections(zkClient);
page.selected = children; // start with the page being the full list page.selected = children; // start with the page being the full list
// activate paging (if disabled) for large collection sets // activate paging (if disabled) for large collection sets
if (page.start == 0 && page.rows == -1 && page.filter == null && children.size() > 10) { if (page.start == 0 && page.rows == -1 && page.filter == null && children.size() > 10) {
page.rows = 20; page.rows = 20;
page.start = 0; page.start = 0;
} }
// apply the name filter if supplied (we don't need to pull state // apply the name filter if supplied (we don't need to pull state
// data from ZK to do name filtering // data from ZK to do name filtering
if (page.filterType == FilterType.name && page.filter != null) if (page.filterType == FilterType.name && page.filter != null)
@ -274,19 +298,19 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
// a little hacky ... we can't select the page when filtering by // a little hacky ... we can't select the page when filtering by
// status until reading all status objects from ZK // status until reading all status objects from ZK
if (page.filterType != FilterType.status) if (page.filterType != FilterType.status)
page.selectPage(children); page.selectPage(children);
return page; return page;
} }
@Override @Override
public int compare(String left, String right) { public int compare(String left, String right) {
if (left == null) if (left == null)
return -1; return -1;
if (left.equals(right)) if (left.equals(right))
return 0; return 0;
// sort lexically unless the two collection names start with the same base prefix // sort lexically unless the two collection names start with the same base prefix
// and end in a number (which is a common enough naming scheme to have direct // and end in a number (which is a common enough naming scheme to have direct
// support for it) // support for it)
@ -300,7 +324,7 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
// both start with the same prefix ... compare indexes // both start with the same prefix ... compare indexes
// using longs here as we don't know how long the 2nd group is // using longs here as we don't know how long the 2nd group is
int leftGroup2 = Integer.parseInt(leftMatcher.group(2)); int leftGroup2 = Integer.parseInt(leftMatcher.group(2));
int rightGroup2 = Integer.parseInt(rightMatcher.group(2)); int rightGroup2 = Integer.parseInt(rightMatcher.group(2));
return (leftGroup2 > rightGroup2) ? 1 : ((leftGroup2 == rightGroup2) ? 0 : -1); return (leftGroup2 > rightGroup2) ? 1 : ((leftGroup2 == rightGroup2) ? 0 : -1);
} }
} }
@ -319,19 +343,16 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
} }
} }
} }
private PagedCollectionSupport pagingSupport; private PagedCollectionSupport pagingSupport;
@Override @Override
public void doGet(HttpServletRequest request, public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
HttpServletResponse response) final SolrParams params = req.getParams();
throws ServletException,IOException { Map<String, String> map = new HashMap<>(1);
// This attribute is set by the SolrDispatchFilter map.put(WT, "raw");
CoreContainer cores = (CoreContainer) request.getAttribute("org.apache.solr.CoreContainer"); map.put(OMIT_HEADER, "true");
if (cores == null) { req.setParams(SolrParams.wrapDefaults(new MapSolrParams(map), params));
throw new ServletException("Missing request attribute org.apache.solr.CoreContainer.");
}
synchronized (this) { synchronized (this) {
if (pagingSupport == null) { if (pagingSupport == null) {
pagingSupport = new PagedCollectionSupport(); pagingSupport = new PagedCollectionSupport();
@ -342,18 +363,6 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
} }
} }
} }
final SolrParams params;
try {
params = SolrRequestParsers.DEFAULT.parse(null, request.getServletPath(), request).getParams();
} catch (Exception e) {
int code=500;
if (e instanceof SolrException) {
code = Math.min(599, Math.max(100, ((SolrException)e).code()));
}
response.sendError(code, e.toString());
return;
}
String path = params.get(PATH); String path = params.get(PATH);
String addr = params.get("addr"); String addr = params.get("addr");
@ -367,10 +376,10 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
String dumpS = params.get("dump"); String dumpS = params.get("dump");
boolean dump = dumpS != null && dumpS.equals("true"); boolean dump = dumpS != null && dumpS.equals("true");
int start = paramAsInt("start", params, 0); int start = params.getInt("start", 0);
int rows = paramAsInt("rows", params, -1); int rows = params.getInt("rows", -1);
String filterType = params.get("filterType"); String filterType = params.get("filterType");
if (filterType != null) { if (filterType != null) {
filterType = filterType.trim().toLowerCase(Locale.ROOT); filterType = filterType.trim().toLowerCase(Locale.ROOT);
@ -378,20 +387,15 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
filterType = null; filterType = null;
} }
FilterType type = (filterType != null) ? FilterType.valueOf(filterType) : FilterType.none; FilterType type = (filterType != null) ? FilterType.valueOf(filterType) : FilterType.none;
String filter = (type != FilterType.none) ? params.get("filter") : null; String filter = (type != FilterType.none) ? params.get("filter") : null;
if (filter != null) { if (filter != null) {
filter = filter.trim(); filter = filter.trim();
if (filter.length() == 0) if (filter.length() == 0)
filter = null; filter = null;
} }
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
Writer out = new FastWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8)); ZKPrinter printer = new ZKPrinter(cores.getZkController(), addr);
ZKPrinter printer = new ZKPrinter(response, out, cores.getZkController(), addr);
printer.detail = detail; printer.detail = detail;
printer.dump = dump; printer.dump = dump;
boolean isGraphView = "graph".equals(params.get("view")); boolean isGraphView = "graph".equals(params.get("view"));
@ -404,65 +408,39 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
} finally { } finally {
printer.close(); printer.close();
} }
rsp.getValues().add(RawResponseWriter.CONTENT,printer);
out.flush();
}
@Override
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,IOException {
doGet(request, response);
}
protected int paramAsInt(final String paramName, final SolrParams params, final int defaultVal) {
int val = defaultVal;
String paramS = params.get(paramName);
if (paramS != null) {
String trimmed = paramS.trim();
if (trimmed.length() > 0) {
try {
val = Integer.parseInt(trimmed);
} catch (NumberFormatException nfe) {
log.warn("Invalid value "+paramS+" passed for parameter "+paramName+"; expected integer!");
}
}
}
return val;
} }
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
// //
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
static class ZKPrinter { static class ZKPrinter implements ContentStream {
static boolean FULLPATH_DEFAULT = false; static boolean FULLPATH_DEFAULT = false;
boolean indent = true; boolean indent = true;
boolean fullpath = FULLPATH_DEFAULT; boolean fullpath = FULLPATH_DEFAULT;
boolean detail = false; boolean detail = false;
boolean dump = false; boolean dump = false;
String addr; // the address passed to us String addr; // the address passed to us
String keeperAddr; // the address we're connected to String keeperAddr; // the address we're connected to
boolean doClose; // close the client after done if we opened it boolean doClose; // close the client after done if we opened it
final HttpServletResponse response; final BAOS baos = new BAOS();
final Writer out; final Writer out = new OutputStreamWriter(baos, StandardCharsets.UTF_8);
SolrZkClient zkClient; SolrZkClient zkClient;
int level; int level;
int maxData = 95; int maxData = 95;
PageOfCollections page; PageOfCollections page;
PagedCollectionSupport pagingSupport; PagedCollectionSupport pagingSupport;
ZkController zkController; ZkController zkController;
public ZKPrinter(HttpServletResponse response, Writer out, ZkController controller, String addr) throws IOException { public ZKPrinter(ZkController controller, String addr) throws IOException {
this.zkController = controller; this.zkController = controller;
this.response = response;
this.out = out;
this.addr = addr; this.addr = addr;
if (addr == null) { if (addr == null) {
@ -500,6 +478,11 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
if (doClose) { if (doClose) {
zkClient.close(); zkClient.close();
} }
try {
out.flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
// main entry point // main entry point
@ -551,7 +534,8 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
} }
void writeError(int code, String msg) throws IOException { void writeError(int code, String msg) throws IOException {
response.setStatus(code); throw new SolrException(ErrorCode.getErrorCode(code), msg);
/*response.setStatus(code);
CharArr chars = new CharArr(); CharArr chars = new CharArr();
JSONWriter w = new JSONWriter(chars, 2); JSONWriter w = new JSONWriter(chars, 2);
@ -567,7 +551,7 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
w.writeString(msg); w.writeString(msg);
w.endObject(); w.endObject();
out.write(chars.toString()); out.write(chars.toString());*/
} }
@ -588,7 +572,7 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
json.writeString("attr"); json.writeString("attr");
json.writeNameSeparator(); json.writeNameSeparator();
json.startObject(); json.startObject();
writeKeyValue(json, "href", "zookeeper?detail=true&path=" + URLEncoder.encode(path, "UTF-8"), true); writeKeyValue(json, "href", "admin/zookeeper?detail=true&path=" + URLEncoder.encode(path, "UTF-8"), true);
json.endObject(); json.endObject();
json.endObject(); json.endObject();
@ -679,12 +663,12 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
boolean printZnode(JSONWriter json, String path) throws IOException { boolean printZnode(JSONWriter json, String path) throws IOException {
try { try {
String dataStr = null; String dataStr = null;
String dataStrErr = null; String dataStrErr = null;
Stat stat = new Stat(); Stat stat = new Stat();
// Trickily, the call to zkClient.getData fills in the stat variable // Trickily, the call to zkClient.getData fills in the stat variable
byte[] data = zkClient.getData(path, null, stat, true); byte[] data = zkClient.getData(path, null, stat, true);
if (null != data) { if (null != data) {
try { try {
dataStr = (new BytesRef(data)).utf8ToString(); dataStr = (new BytesRef(data)).utf8ToString();
@ -697,7 +681,7 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
// we've already pulled the data for /clusterstate.json from ZooKeeper above, // we've already pulled the data for /clusterstate.json from ZooKeeper above,
// but it needs to be parsed into a map so we can lookup collection states before // but it needs to be parsed into a map so we can lookup collection states before
// trying to find them in the /collections/?/state.json znode // trying to find them in the /collections/?/state.json znode
Map<String,Object> clusterstateJsonMap = null; Map<String, Object> clusterstateJsonMap = null;
if (dataStr != null) { if (dataStr != null) {
try { try {
clusterstateJsonMap = (Map<String, Object>) ObjectBuilder.fromJSON(dataStr); clusterstateJsonMap = (Map<String, Object>) ObjectBuilder.fromJSON(dataStr);
@ -708,73 +692,73 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
} else { } else {
clusterstateJsonMap = Utils.makeMap(); clusterstateJsonMap = Utils.makeMap();
} }
// fetch the requested page of collections and then retrieve the state for each // fetch the requested page of collections and then retrieve the state for each
page = pagingSupport.fetchPage(page, zkClient); page = pagingSupport.fetchPage(page, zkClient);
// keep track of how many collections match the filter // keep track of how many collections match the filter
boolean applyStatusFilter = boolean applyStatusFilter =
(page.filterType == FilterType.status && page.filter != null); (page.filterType == FilterType.status && page.filter != null);
List<String> matchesStatusFilter = applyStatusFilter ? new ArrayList<String>() : null; List<String> matchesStatusFilter = applyStatusFilter ? new ArrayList<String>() : null;
Set<String> liveNodes = applyStatusFilter ? Set<String> liveNodes = applyStatusFilter ?
zkController.getZkStateReader().getClusterState().getLiveNodes() : null; zkController.getZkStateReader().getClusterState().getLiveNodes() : null;
SortedMap<String,Object> collectionStates = new TreeMap<String,Object>(pagingSupport); SortedMap<String, Object> collectionStates = new TreeMap<String, Object>(pagingSupport);
for (String collection : page.selected) { for (String collection : page.selected) {
Object collectionState = clusterstateJsonMap.get(collection); Object collectionState = clusterstateJsonMap.get(collection);
if (collectionState != null) { if (collectionState != null) {
// collection state was in /clusterstate.json // collection state was in /clusterstate.json
if (applyStatusFilter) { if (applyStatusFilter) {
// verify this collection matches the status filter // verify this collection matches the status filter
if (page.matchesStatusFilter((Map<String,Object>)collectionState,liveNodes)) { if (page.matchesStatusFilter((Map<String, Object>) collectionState, liveNodes)) {
matchesStatusFilter.add(collection); matchesStatusFilter.add(collection);
collectionStates.put(collection, collectionState); collectionStates.put(collection, collectionState);
} }
} else { } else {
collectionStates.put(collection, collectionState); collectionStates.put(collection, collectionState);
} }
} else { } else {
// looks like an external collection ... // looks like an external collection ...
String collStatePath = String.format(Locale.ROOT, "/collections/%s/state.json", collection); String collStatePath = String.format(Locale.ROOT, "/collections/%s/state.json", collection);
String childDataStr = null; String childDataStr = null;
try { try {
byte[] childData = zkClient.getData(collStatePath, null, null, true); byte[] childData = zkClient.getData(collStatePath, null, null, true);
if (childData != null) if (childData != null)
childDataStr = (new BytesRef(childData)).utf8ToString(); childDataStr = (new BytesRef(childData)).utf8ToString();
} catch (KeeperException.NoNodeException nne) { } catch (KeeperException.NoNodeException nne) {
log.warn("State for collection "+collection+ log.warn("State for collection " + collection +
" not found in /clusterstate.json or /collections/"+collection+"/state.json!"); " not found in /clusterstate.json or /collections/" + collection + "/state.json!");
} catch (Exception childErr) { } catch (Exception childErr) {
log.error("Failed to get "+collStatePath+" due to: "+childErr); log.error("Failed to get " + collStatePath + " due to: " + childErr);
} }
if (childDataStr != null) { if (childDataStr != null) {
Map<String,Object> extColl = (Map<String,Object>)ObjectBuilder.fromJSON(childDataStr); Map<String, Object> extColl = (Map<String, Object>) ObjectBuilder.fromJSON(childDataStr);
collectionState = extColl.get(collection); collectionState = extColl.get(collection);
if (applyStatusFilter) { if (applyStatusFilter) {
// verify this collection matches the filtered state // verify this collection matches the filtered state
if (page.matchesStatusFilter((Map<String,Object>)collectionState,liveNodes)) { if (page.matchesStatusFilter((Map<String, Object>) collectionState, liveNodes)) {
matchesStatusFilter.add(collection); matchesStatusFilter.add(collection);
collectionStates.put(collection, collectionState); collectionStates.put(collection, collectionState);
} }
} else { } else {
collectionStates.put(collection, collectionState); collectionStates.put(collection, collectionState);
} }
} }
} }
} }
if (applyStatusFilter) { if (applyStatusFilter) {
// update the paged navigation info after applying the status filter // update the paged navigation info after applying the status filter
page.selectPage(matchesStatusFilter); page.selectPage(matchesStatusFilter);
// rebuild the Map of state data // rebuild the Map of state data
SortedMap<String,Object> map = new TreeMap<String,Object>(pagingSupport); SortedMap<String, Object> map = new TreeMap<String, Object>(pagingSupport);
for (String next : page.selected) for (String next : page.selected)
map.put(next, collectionStates.get(next)); map.put(next, collectionStates.get(next));
collectionStates = map; collectionStates = map;
} }
if (collectionStates != null) { if (collectionStates != null) {
CharArr out = new CharArr(); CharArr out = new CharArr();
new JSONWriter(out, 2).write(collectionStates); new JSONWriter(out, 2).write(collectionStates);
@ -826,5 +810,41 @@ public final class ZookeeperInfoServlet extends BaseSolrServlet {
} }
return true; return true;
} }
/* @Override
public void write(OutputStream os) throws IOException {
ByteBuffer bytes = baos.getByteBuffer();
os.write(bytes.array(),0,bytes.limit());
}
*/
@Override
public String getName() {
return null;
}
@Override
public String getSourceInfo() {
return null;
}
@Override
public String getContentType() {
return JSONResponseWriter.CONTENT_TYPE_JSON_UTF8;
}
@Override
public Long getSize() {
return null;
}
@Override
public InputStream getStream() throws IOException {
return new ByteBufferInputStream(baos.getByteBuffer());
}
@Override
public Reader getReader() throws IOException {
return null;
}
} }
} }

View File

@ -42,7 +42,7 @@ import org.apache.solr.search.SolrReturnFields;
*/ */
public class JSONResponseWriter implements QueryResponseWriter { public class JSONResponseWriter implements QueryResponseWriter {
static String CONTENT_TYPE_JSON_UTF8 = "application/json; charset=UTF-8"; public static String CONTENT_TYPE_JSON_UTF8 = "application/json; charset=UTF-8";
private String contentType = CONTENT_TYPE_JSON_UTF8; private String contentType = CONTENT_TYPE_JSON_UTF8;

View File

@ -169,6 +169,7 @@ public interface CommonParams {
public static final String CONFIGSETS_HANDLER_PATH = "/admin/configs"; public static final String CONFIGSETS_HANDLER_PATH = "/admin/configs";
public static final String AUTHZ_PATH = "/admin/authorization"; public static final String AUTHZ_PATH = "/admin/authorization";
public static final String AUTHC_PATH = "/admin/authentication"; public static final String AUTHC_PATH = "/admin/authentication";
public static final String ZK_PATH = "/admin/zookeeper";
/** valid values for: <code>echoParams</code> */ /** valid values for: <code>echoParams</code> */
public enum EchoParamStyle { public enum EchoParamStyle {

View File

@ -75,11 +75,6 @@
<url-pattern>/*</url-pattern> <url-pattern>/*</url-pattern>
</filter-mapping> </filter-mapping>
<servlet>
<servlet-name>Zookeeper</servlet-name>
<servlet-class>org.apache.solr.servlet.ZookeeperInfoServlet</servlet-class>
</servlet>
<servlet> <servlet>
<servlet-name>LoadAdminUI</servlet-name> <servlet-name>LoadAdminUI</servlet-name>
<servlet-class>org.apache.solr.servlet.LoadAdminUiServlet</servlet-class> <servlet-class>org.apache.solr.servlet.LoadAdminUiServlet</servlet-class>
@ -101,10 +96,10 @@
<servlet-class>org.apache.solr.servlet.RedirectServlet</servlet-class> <servlet-class>org.apache.solr.servlet.RedirectServlet</servlet-class>
<init-param> <init-param>
<param-name>destination</param-name> <param-name>destination</param-name>
<param-value>${context}/zookeeper</param-value> <param-value>${context}/admin/zookeeper</param-value>
</init-param> </init-param>
</servlet> </servlet>
<servlet> <servlet>
<servlet-name>RedirectLogging</servlet-name> <servlet-name>RedirectLogging</servlet-name>
<servlet-class>org.apache.solr.servlet.RedirectServlet</servlet-class> <servlet-class>org.apache.solr.servlet.RedirectServlet</servlet-class>
@ -135,17 +130,15 @@
<servlet-name>RedirectOldZookeeper</servlet-name> <servlet-name>RedirectOldZookeeper</servlet-name>
<url-pattern>/zookeeper.jsp</url-pattern> <url-pattern>/zookeeper.jsp</url-pattern>
</servlet-mapping> </servlet-mapping>
<servlet-mapping>
<servlet-name>RedirectOldZookeeper</servlet-name>
<url-pattern>/zookeeper</url-pattern>
</servlet-mapping>
<servlet-mapping> <servlet-mapping>
<servlet-name>RedirectLogging</servlet-name> <servlet-name>RedirectLogging</servlet-name>
<url-pattern>/logging</url-pattern> <url-pattern>/logging</url-pattern>
</servlet-mapping> </servlet-mapping>
<!-- Servlet Mapping -->
<servlet-mapping>
<servlet-name>Zookeeper</servlet-name>
<url-pattern>/zookeeper</url-pattern>
</servlet-mapping>
<servlet-mapping> <servlet-mapping>
<servlet-name>LoadAdminUI</servlet-name> <servlet-name>LoadAdminUI</servlet-name>
<url-pattern>/admin.html</url-pattern> <url-pattern>/admin.html</url-pattern>

View File

@ -60,7 +60,7 @@ solrAdminServices.factory('System',
}]) }])
.factory('Zookeeper', .factory('Zookeeper',
['$resource', function($resource) { ['$resource', function($resource) {
return $resource('/solr/zookeeper', {wt:'json', _:Date.now()}, { return $resource('/solr/admin/zookeeper', {wt:'json', _:Date.now()}, {
"simple": {}, "simple": {},
"dump": {params: {dump: "true"}}, "dump": {params: {dump: "true"}},
"liveNodes": {params: {path: '/live_nodes'}}, "liveNodes": {params: {path: '/live_nodes'}},

View File

@ -91,7 +91,7 @@ var init_debug = function( cloud_element )
$.ajax $.ajax
( (
{ {
url : app.config.solr_path + '/zookeeper?wt=json&dump=true', url : app.config.solr_path + '/admin/zookeeper?wt=json&dump=true',
dataType : 'text', dataType : 'text',
context : debug_element, context : debug_element,
beforeSend : function( xhr, settings ) beforeSend : function( xhr, settings )
@ -455,7 +455,7 @@ var prepare_graph = function( graph_element, callback )
$.ajax $.ajax
( (
{ {
url : app.config.solr_path + '/zookeeper?wt=json&path=%2Flive_nodes', url : app.config.solr_path + '/admin/zookeeper?wt=json&path=%2Flive_nodes',
dataType : 'json', dataType : 'json',
success : function( response, text_status, xhr ) success : function( response, text_status, xhr )
{ {
@ -467,7 +467,7 @@ var prepare_graph = function( graph_element, callback )
var start = $( '#cloudGraphPagingStart' ).val(); var start = $( '#cloudGraphPagingStart' ).val();
var rows = $( '#cloudGraphPagingRows' ).val(); var rows = $( '#cloudGraphPagingRows' ).val();
var clusterStateUrl = app.config.solr_path + '/zookeeper?wt=json&detail=true&path=%2Fclusterstate.json&view=graph'; var clusterStateUrl = app.config.solr_path + '/admin/zookeeper?wt=json&detail=true&path=%2Fclusterstate.json&view=graph';
if (start && rows) if (start && rows)
clusterStateUrl += ('&start='+start+'&rows='+rows); clusterStateUrl += ('&start='+start+'&rows='+rows);
@ -591,7 +591,7 @@ var init_tree = function( tree_element )
$.ajax $.ajax
( (
{ {
url : app.config.solr_path + '/zookeeper?wt=json', url : app.config.solr_path + '/admin/zookeeper?wt=json',
dataType : 'json', dataType : 'json',
context : tree_element, context : tree_element,
beforeSend : function( xhr, settings ) beforeSend : function( xhr, settings )
@ -859,7 +859,7 @@ sammy.get
$.ajax $.ajax
( (
{ {
url : app.config.solr_path + '/zookeeper?wt=json', url : app.config.solr_path + '/admin/zookeeper?wt=json',
dataType : 'json', dataType : 'json',
context : cloud_element, context : cloud_element,
success : function( response, text_status, xhr ) success : function( response, text_status, xhr )