From 4fab579c2ae8734d774157907cb03e8cb4bbd733 Mon Sep 17 00:00:00 2001 From: Noble Paul Date: Thu, 27 Nov 2014 13:16:22 +0000 Subject: [PATCH] SOLR-6607 Managing requesthandlers through API git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1642141 13f79535-47bb-0310-9956-ffa450edef68 --- solr/CHANGES.txt | 2 + .../org/apache/solr/cloud/ZkController.java | 6 +- .../org/apache/solr/core/ConfigOverlay.java | 31 +++++ .../java/org/apache/solr/core/PluginInfo.java | 35 ++++-- .../org/apache/solr/core/RequestHandlers.java | 11 +- .../java/org/apache/solr/core/SolrConfig.java | 3 +- .../solr/handler/SolrConfigHandler.java | 67 ++++++++++- .../apache/solr/util/CommandOperation.java | 11 ++ .../solr/core/TestSolrConfigHandler.java | 113 +++++++++++++++--- .../handler/TestSolrConfigHandlerCloud.java | 72 +++++++++++ .../TestSolrConfigHandlerConcurrent.java | 5 +- 11 files changed, 317 insertions(+), 39 deletions(-) create mode 100644 solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index b01063797ea..4962e64d548 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -217,6 +217,8 @@ New Features * SOLR-6533: Support editing common solrconfig.xml values (Noble Paul) +* SOLR-6607: Managing requesthandlers throuh API (Noble Paul) + Bug Fixes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java index 0a7a7d03d41..b6655687493 100644 --- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java +++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java @@ -2235,17 +2235,19 @@ public final class ZkController { log.info("Watcher on {} is removed ", zkDir); return; } - final Set listeners = confDirectoryListeners.get(zkDir); + Set listeners = confDirectoryListeners.get(zkDir); if (listeners != null && !listeners.isEmpty()) { + final Set listenersCopy = new HashSet<>(listeners); new Thread() { //run these in a separate thread because this can be long running public void run() { - for (final Runnable listener : listeners) + for (final Runnable listener : listenersCopy) { try { listener.run(); } catch (Exception e) { log.warn("listener throws error", e); } + } } }.start(); } diff --git a/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java b/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java index 3b6b0441583..a9dcff5d293 100644 --- a/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java +++ b/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java @@ -28,17 +28,22 @@ import java.util.Map; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.ZkStateReader; +import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.util.StrUtils; +import org.apache.solr.request.SolrRequestHandler; import org.noggit.CharArr; import org.noggit.JSONParser; import org.noggit.JSONWriter; import org.noggit.ObjectBuilder; +import static org.apache.solr.common.params.CoreAdminParams.NAME; + public class ConfigOverlay implements MapSerializable{ private final int znodeVersion ; private Map data; private Map props; private Map userProps; + private Map reqHandlers; public ConfigOverlay(Map jsonObj, int znodeVersion){ if(jsonObj == null) jsonObj= Collections.EMPTY_MAP; @@ -48,6 +53,8 @@ public class ConfigOverlay implements MapSerializable{ if(props == null) props= Collections.EMPTY_MAP; userProps = (Map) data.get("userProps"); if(userProps == null) userProps= Collections.EMPTY_MAP; + reqHandlers = (Map) data.get(SolrRequestHandler.TYPE); + if(reqHandlers == null) reqHandlers = new LinkedHashMap<>(); } public Object getXPathProperty(String xpath){ @@ -255,4 +262,28 @@ public class ConfigOverlay implements MapSerializable{ result.putAll(data); return result; } + + public Map getReqHandlers() { + return Collections.unmodifiableMap(reqHandlers); + } + + public ConfigOverlay addReqHandler(Map info) { + ConfigOverlay copy = copyOverLayWithReqHandler(); + copy.reqHandlers.put((String)info.get(NAME) , info); + return copy; + } + + private ConfigOverlay copyOverLayWithReqHandler() { + LinkedHashMap newmap = new LinkedHashMap<>(data); + ConfigOverlay copy = new ConfigOverlay(newmap, znodeVersion); + newmap.put(SolrRequestHandler.TYPE, copy.reqHandlers = new LinkedHashMap<>(reqHandlers)); + return copy; + } + + public ConfigOverlay deleteHandler(String name) { + ConfigOverlay copy = copyOverLayWithReqHandler(); + copy.reqHandlers.remove(name); + return copy; + + } } diff --git a/solr/core/src/java/org/apache/solr/core/PluginInfo.java b/solr/core/src/java/org/apache/solr/core/PluginInfo.java index 007dadfda5f..18a413a867d 100644 --- a/solr/core/src/java/org/apache/solr/core/PluginInfo.java +++ b/solr/core/src/java/org/apache/solr/core/PluginInfo.java @@ -22,8 +22,12 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.util.*; + +import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; +import static org.apache.solr.common.params.CoreAdminParams.NAME; +import static org.apache.solr.schema.FieldType.CLASS_NAME; /** * An Object which represents a Plugin of any type @@ -38,8 +42,8 @@ public class PluginInfo implements MapSerializable{ public PluginInfo(String type, Map attrs ,NamedList initArgs, List children) { this.type = type; - this.name = attrs.get("name"); - this.className = attrs.get("class"); + this.name = attrs.get(NAME); + this.className = attrs.get(CLASS_NAME); this.initArgs = initArgs; attributes = unmodifiableMap(attrs); this.children = children == null ? Collections.emptyList(): unmodifiableList(children); @@ -49,14 +53,27 @@ public class PluginInfo implements MapSerializable{ public PluginInfo(Node node, String err, boolean requireName, boolean requireClass) { type = node.getNodeName(); - name = DOMUtil.getAttr(node, "name", requireName ? err : null); - className = DOMUtil.getAttr(node, "class", requireClass ? err : null); + name = DOMUtil.getAttr(node, NAME, requireName ? err : null); + className = DOMUtil.getAttr(node, CLASS_NAME, requireClass ? err : null); initArgs = DOMUtil.childNodesToNamedList(node); attributes = unmodifiableMap(DOMUtil.toMap(node.getAttributes())); children = loadSubPlugins(node); isFromSolrConfig = true; } + public PluginInfo(String type, Map map) { + LinkedHashMap m = new LinkedHashMap<>(map); + NamedList nl = new NamedList(); + for (String s : asList(DEFAULTS, APPENDS, INVARIANTS)) if (m.get(s) != null) nl.add(s, map.remove(s)); + this.type = type; + this.name = (String) m.get(NAME); + this.className = (String) m.get(CLASS_NAME); + this.initArgs = nl; + attributes = unmodifiableMap(m); + this.children = Collections.emptyList(); + isFromSolrConfig = true; + } + private List loadSubPlugins(Node node) { List children = new ArrayList<>(); //if there is another sub tag with a non namedlist tag that has to be another plugin @@ -131,11 +148,11 @@ public class PluginInfo implements MapSerializable{ public static final PluginInfo EMPTY_INFO = new PluginInfo("",Collections.emptyMap(), new NamedList(),Collections.emptyList()); private static final HashSet NL_TAGS = new HashSet<> - (Arrays.asList("lst", "arr", - "bool", - "str", - "int","long", - "float","double")); + (asList("lst", "arr", + "bool", + "str", + "int", "long", + "float", "double")); public static final String DEFAULTS = "defaults"; public static final String APPENDS = "appends"; public static final String INVARIANTS = "invariants"; diff --git a/solr/core/src/java/org/apache/solr/core/RequestHandlers.java b/solr/core/src/java/org/apache/solr/core/RequestHandlers.java index 64fd248dbc8..7523f8713ca 100644 --- a/solr/core/src/java/org/apache/solr/core/RequestHandlers.java +++ b/solr/core/src/java/org/apache/solr/core/RequestHandlers.java @@ -143,12 +143,15 @@ public final class RequestHandlers { List implicits = PluginsRegistry.getHandlers(core); // use link map so we iterate in the same order Map handlers = new LinkedHashMap<>(); - Map implicitInfoMap= new HashMap<>(); + Map infoMap= new LinkedHashMap<>(); //deduping implicit and explicit requesthandlers - for (PluginInfo info : implicits) implicitInfoMap.put(info.name,info); + for (PluginInfo info : implicits) infoMap.put(info.name,info); for (PluginInfo info : config.getPluginInfos(SolrRequestHandler.class.getName())) - if(implicitInfoMap.containsKey(info.name)) implicitInfoMap.remove(info.name); - ArrayList infos = new ArrayList<>(implicitInfoMap.values()); + if(infoMap.containsKey(info.name)) infoMap.remove(info.name); + for (Map.Entry e : core.getSolrConfig().getOverlay().getReqHandlers().entrySet()) + infoMap.put((String)e.getKey(), new PluginInfo(SolrRequestHandler.TYPE, (Map)e.getValue())); + + ArrayList infos = new ArrayList<>(infoMap.values()); infos.addAll(config.getPluginInfos(SolrRequestHandler.class.getName())); for (PluginInfo info : infos) { try { diff --git a/solr/core/src/java/org/apache/solr/core/SolrConfig.java b/solr/core/src/java/org/apache/solr/core/SolrConfig.java index 70a5af0ef26..18d1a8c766a 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrConfig.java +++ b/solr/core/src/java/org/apache/solr/core/SolrConfig.java @@ -334,13 +334,14 @@ public class SolrConfig extends Config implements MapSerializable{ in = loader.openResource(ConfigOverlay.RESOURCE_NAME); } catch (IOException e) { //no problem no overlay.json file - return new ConfigOverlay(Collections.EMPTY_MAP,0); + return new ConfigOverlay(Collections.EMPTY_MAP,-1); } try { int version = 0; //will be always 0 for file based resourceloader if (in instanceof ZkSolrResourceLoader.ZkByteArrayInputStream) { version = ((ZkSolrResourceLoader.ZkByteArrayInputStream) in).getStat().getVersion(); + log.info("config overlay loaded . version : {} ", version); } Map m = (Map) ObjectBuilder.getVal(new JSONParser(new InputStreamReader(in, StandardCharsets.UTF_8))); return new ConfigOverlay(m,version); diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java index 661b25e1c00..615d2d05453 100644 --- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java @@ -51,6 +51,7 @@ import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.schema.FieldType; import org.apache.solr.schema.SchemaManager; import org.apache.solr.util.CommandOperation; import org.apache.solr.util.plugin.SolrCoreAware; @@ -63,8 +64,10 @@ import static java.text.MessageFormat.format; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.apache.solr.common.cloud.ZkNodeProps.makeMap; +import static org.apache.solr.common.params.CoreAdminParams.NAME; import static org.apache.solr.core.ConfigOverlay.NOT_EDITABLE; import static org.apache.solr.core.PluginInfo.DEFAULTS; +import static org.apache.solr.schema.FieldType.CLASS_NAME; public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAware{ public static final Logger log = LoggerFactory.getLogger(SolrConfigHandler.class); @@ -106,7 +109,7 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa return new Runnable() { @Override public void run() { - log.info("config update_listener called"); + log.info("config update listener called for core {}", coreName); SolrZkClient zkClient = cc.getZkController().getZkClient(); int solrConfigversion,overlayVersion; try (SolrCore core = cc.getCore(coreName)) { @@ -117,7 +120,7 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa if (checkStale(zkClient, overlayPath, solrConfigversion) || checkStale(zkClient, solrConfigPath, overlayVersion)) { - log.info("core reload"); + log.info("core reload {}",coreName); cc.reload(coreName); } } @@ -128,7 +131,7 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa try { Stat stat = zkClient.exists(zkPath, null, true); if(stat == null){ - if(currentVersion>0) return true; + if(currentVersion > -1) return true; return false; } if (stat.getVersion() > currentVersion) { @@ -198,7 +201,7 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa for (CommandOperation op : ops) opsCopy.add(op.getCopy()); try { handleCommands(opsCopy, overlay); - break; + break;//succeeded . so no need to go over the loop again } catch (ZkController.ResourceModifiedInZkException e) { //retry log.info("Race condition, the node is modified in ZK by someone else " +e.getMessage()); @@ -226,6 +229,13 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa case UNSET_USER_PROPERTY: overlay = applyUnsetUserProp(op, overlay); break; + case UPDATE_REQHANDLER: + case CREATE_REQHANDLER: + overlay = applyRequestHandler(op, overlay); + break; + case DELETE_REQHANDLER: + overlay = applyDeleteHandler(op,overlay); + break; } } List errs = CommandOperation.captureErrors(ops); @@ -246,6 +256,50 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa } + private ConfigOverlay applyDeleteHandler(CommandOperation op, ConfigOverlay overlay) { + String name = op.getStr(CommandOperation.ROOT_OBJ); + if(op.hasError()) return overlay; + if(overlay.getReqHandlers().containsKey(name)){ + return overlay.deleteHandler(name); + } else { + op.addError(MessageFormat.format("NO such requestHandler ''{0}'' ",name)); + return overlay; + } + + } + + private ConfigOverlay applyRequestHandler(CommandOperation op, ConfigOverlay overlay) { + String name=op.getStr(NAME); + op.getStr(CLASS_NAME); + op.getMap(PluginInfo.DEFAULTS, null); + op.getMap(PluginInfo.INVARIANTS,null); + op.getMap(PluginInfo.APPENDS,null); + if(op.hasError()) return overlay; + + + if(CREATE_REQHANDLER.equals(op.name)) { + if (overlay.getReqHandlers().containsKey(name)) { + op.addError(MessageFormat.format(" ''{0}'' already exists . Do an ''{1}'' , if you want to change it ", name, UPDATE_REQHANDLER)); + return overlay; + } else { + return overlay.addReqHandler(op.getDataMap()); + } + } else if(UPDATE_REQHANDLER.equals(op.name)){ + if (!overlay.getReqHandlers().containsKey(name)) { + op.addError(MessageFormat.format(" ''{0}'' does not exist . Do an ''{1}'' , if you want to create it ", name, CREATE_REQHANDLER)); + return overlay; + } else { + return overlay.addReqHandler(op.getDataMap()); + + } + } + + return overlay; + + + + } + private ConfigOverlay applySetUserProp(CommandOperation op, ConfigOverlay overlay) { Map m = op.getDataMap(); if(op.hasError()) return overlay; @@ -351,7 +405,8 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa public static final String UNSET_PROPERTY = "unset-property"; public static final String SET_USER_PROPERTY = "set-user-property"; public static final String UNSET_USER_PROPERTY = "unset-user-property"; - - + public static final String CREATE_REQHANDLER = "create-requesthandler"; + public static final String DELETE_REQHANDLER = "delete-requesthandler"; + public static final String UPDATE_REQHANDLER = "update-requesthandler"; } diff --git a/solr/core/src/java/org/apache/solr/util/CommandOperation.java b/solr/core/src/java/org/apache/solr/util/CommandOperation.java index 02a1fcc02f0..89fdb37bcf4 100644 --- a/solr/core/src/java/org/apache/solr/util/CommandOperation.java +++ b/solr/core/src/java/org/apache/solr/util/CommandOperation.java @@ -214,4 +214,15 @@ public class CommandOperation { return new CommandOperation(name,commandData); } + public Map getMap(String key, Map def) { + Object o =getMapVal(key); + if(o==null) return def; + if ( !(o instanceof Map)) { + addError(MessageFormat.format("''{0}'' must be a map", key)); + return def; + } else { + return (Map) o; + + } + } } diff --git a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java index 3fe6d744b01..b74fec15e2e 100644 --- a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java +++ b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java @@ -19,15 +19,25 @@ package org.apache.solr.core; import java.io.File; +import java.io.IOException; import java.io.StringReader; -import java.util.List; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; +import java.util.concurrent.TimeUnit; import com.google.common.collect.ImmutableList; import org.apache.commons.io.FileUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.util.EntityUtils; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.solrj.impl.CloudSolrServer; +import org.apache.solr.client.solrj.impl.HttpSolrServer; +import org.apache.solr.handler.TestSolrConfigHandlerConcurrent; import org.apache.solr.util.RestTestBase; import org.apache.solr.util.RestTestHarness; import org.eclipse.jetty.servlet.ServletHolder; @@ -77,13 +87,11 @@ public class TestSolrConfigHandler extends RestTestBase { public void testProperty() throws Exception{ + RestTestHarness harness = restTestHarness; String payload= "{\n" + " 'set-property' : { 'updateHandler.autoCommit.maxDocs':100, 'updateHandler.autoCommit.maxTime':10 } \n" + " }"; - RestTestHarness harness = restTestHarness; - String response = harness.post("/config?wt=json", SolrTestCaseJ4.json(payload)); - Map map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response))); - assertNull(response, map.get("errors")); + runConfigCommand( harness,"/config?wt=json", payload); Map m = (Map) getRespMap("/config/overlay?wt=json" ,harness).get("overlay"); Map props = (Map) m.get("props"); @@ -99,9 +107,7 @@ public class TestSolrConfigHandler extends RestTestBase { payload= "{\n" + " 'unset-property' : 'updateHandler.autoCommit.maxDocs'} \n" + " }"; - response = harness.post("/config?wt=json", SolrTestCaseJ4.json(payload)); - map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response))); - assertNull(response, map.get("errors")); + runConfigCommand(harness, "/config?wt=json", payload); m = (Map) getRespMap("/config/overlay?wt=json" ,harness).get("overlay"); props = (Map) m.get("props"); @@ -111,14 +117,12 @@ public class TestSolrConfigHandler extends RestTestBase { } public void testUserProp() throws Exception{ + RestTestHarness harness = restTestHarness; String payload= "{\n" + " 'set-user-property' : { 'my.custom.variable.a':'MODIFIEDA'," + " 'my.custom.variable.b':'MODIFIEDB' } \n" + " }"; - RestTestHarness harness = restTestHarness; - String response = harness.post("/config?wt=json", SolrTestCaseJ4.json(payload)); - Map map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response))); - assertNull(response, map.get("errors")); + runConfigCommand(harness,"/config?wt=json", payload); Map m = (Map) getRespMap("/config/overlay?wt=json" ,harness).get("overlay"); Map props = (Map) m.get("userProps"); @@ -132,14 +136,93 @@ public class TestSolrConfigHandler extends RestTestBase { assertEquals("MODIFIEDA", m.get("a")); assertEquals("MODIFIEDB", m.get("b")); - - } + public void testReqHandlerAPIs() throws Exception { + reqhandlertests(restTestHarness, null,null); + } + + private static void runConfigCommand(RestTestHarness harness, String uri, String payload) throws IOException { + String response = harness.post(uri, SolrTestCaseJ4.json(payload)); + Map map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response))); + assertNull(response, map.get("errors")); + } + + + public static void reqhandlertests(RestTestHarness writeHarness,String testServerBaseUrl, CloudSolrServer cloudSolrServer) throws Exception { + String payload = "{\n" + + "'create-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' , 'startup' : 'lazy'}\n" + + "}"; + runConfigCommand(writeHarness,"/config?wt=json", payload); + + boolean success = false; + long startTime = System.nanoTime(); + long maxTimeoutSeconds = 10; + while ( TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) { + String uri = "/config/overlay?wt=json"; + Map m = testServerBaseUrl ==null? getRespMap(uri,writeHarness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl+uri ,cloudSolrServer) ; + if("lazy".equals( ConfigOverlay.getObjectByPath(m, true, Arrays.asList("overlay", "requestHandler", "/x","startup")))) { + Map map = getRespMap("/x?wt=json",writeHarness); + if(map.containsKey("params")) { + success = true; + break; + } + } + Thread.sleep(100); + + } + + assertTrue( "Could not register requestHandler ", success); + + payload = "{\n" + + "'update-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' , 'startup' : 'lazy' , 'a':'b'}\n" + + "}"; + runConfigCommand(writeHarness,"/config?wt=json", payload); + + success = false; + startTime = System.nanoTime(); + maxTimeoutSeconds = 10; + while ( TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) { + String uri = "/config/overlay?wt=json"; + Map m = testServerBaseUrl ==null? getRespMap(uri,writeHarness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl+uri ,cloudSolrServer) ; + if("b".equals( ConfigOverlay.getObjectByPath(m, true, Arrays.asList("overlay", "requestHandler", "/x","a")))) { + success = true; + break; + } + Thread.sleep(100); + + } + + assertTrue( "Could not update requestHandler ", success); + + payload = "{\n" + + "'delete-requesthandler' : '/x'" + + "}"; + runConfigCommand(writeHarness,"/config?wt=json", payload); + success = false; + startTime = System.nanoTime(); + maxTimeoutSeconds = 10; + while ( TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) { + String uri = "/config/overlay?wt=json"; + Map m = testServerBaseUrl ==null? getRespMap(uri,writeHarness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl+uri ,cloudSolrServer) ; + if(null == ConfigOverlay.getObjectByPath(m, true, Arrays.asList("overlay", "requestHandler", "/x","a"))) { + success = true; + break; + } + Thread.sleep(100); + + } + assertTrue( "Could not delete requestHandler ", success); + + } public static Map getRespMap(String path, RestTestHarness restHarness) throws Exception { String response = restHarness.query(path); - return (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response))); + try { + return (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response))); + } catch (JSONParser.ParseException e) { + return Collections.emptyMap(); + } } } diff --git a/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java b/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java new file mode 100644 index 00000000000..b853d17a7da --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerCloud.java @@ -0,0 +1,72 @@ +package org.apache.solr.handler; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + + +import java.util.ArrayList; +import java.util.List; + +import org.apache.solr.client.solrj.SolrServer; +import org.apache.solr.client.solrj.impl.HttpSolrServer; +import org.apache.solr.cloud.AbstractFullDistribZkTestBase; +import org.apache.solr.common.cloud.DocCollection; +import org.apache.solr.common.cloud.Replica; +import org.apache.solr.common.cloud.Slice; +import org.apache.solr.common.cloud.ZkStateReader; +import org.apache.solr.core.TestSolrConfigHandler; +import org.apache.solr.util.RESTfulServerProvider; +import org.apache.solr.util.RestTestHarness; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestSolrConfigHandlerCloud extends AbstractFullDistribZkTestBase { + static final Logger log = LoggerFactory.getLogger(TestSolrConfigHandlerCloud.class); + private List restTestHarnesses = new ArrayList<>(); + + private void setupHarnesses() { + for (final SolrServer client : clients) { + RestTestHarness harness = new RestTestHarness(new RESTfulServerProvider() { + @Override + public String getBaseURL() { + return ((HttpSolrServer)client).getBaseURL(); + } + }); + restTestHarnesses.add(harness); + } + } + + @Override + public void doTest() throws Exception { + setupHarnesses(); + testReqHandlerAPIs(); + + } + + private void testReqHandlerAPIs() throws Exception { + DocCollection coll = cloudClient.getZkStateReader().getClusterState().getCollection("collection1"); + List urls = new ArrayList<>(); + for (Slice slice : coll.getSlices()) { + for (Replica replica : slice.getReplicas()) + urls.add(""+replica.get(ZkStateReader.BASE_URL_PROP) + "/"+replica.get(ZkStateReader.CORE_NAME_PROP)); + } + + RestTestHarness writeHarness = restTestHarnesses.get(random().nextInt(restTestHarnesses.size())); + String testServerBaseUrl = urls.get(random().nextInt(urls.size())); + TestSolrConfigHandler.reqhandlertests(writeHarness, testServerBaseUrl , cloudClient); + } +} diff --git a/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerConcurrent.java b/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerConcurrent.java index be018bbc194..4ad95c4be41 100644 --- a/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerConcurrent.java +++ b/solr/core/src/test/org/apache/solr/handler/TestSolrConfigHandlerConcurrent.java @@ -33,6 +33,7 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.util.EntityUtils; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.client.solrj.SolrServer; +import org.apache.solr.client.solrj.impl.CloudSolrServer; import org.apache.solr.client.solrj.impl.HttpSolrServer; import org.apache.solr.cloud.AbstractFullDistribZkTestBase; import org.apache.solr.common.cloud.DocCollection; @@ -164,7 +165,7 @@ public class TestSolrConfigHandlerConcurrent extends AbstractFullDistribZkTestBa while ( TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) { Thread.sleep(100); errmessages.clear(); - Map respMap = getAsMap(url+"/config/overlay?wt=json"); + Map respMap = getAsMap(url+"/config/overlay?wt=json", cloudClient); Map m = (Map) respMap.get("overlay"); if(m!= null) m = (Map) m.get("props"); if(m == null) { @@ -191,7 +192,7 @@ public class TestSolrConfigHandlerConcurrent extends AbstractFullDistribZkTestBa } - private Map getAsMap(String uri) throws Exception { + public static Map getAsMap(String uri, CloudSolrServer cloudClient) throws Exception { HttpGet get = new HttpGet(uri) ; HttpEntity entity = null; try {