| /* |
| * 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. |
| */ |
| package org.apache.solr.core; |
| |
| import java.io.*; |
| import java.lang.invoke.MethodHandles; |
| import java.nio.ByteBuffer; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| import java.util.concurrent.TimeUnit; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipOutputStream; |
| |
| import com.google.common.collect.ImmutableList; |
| import org.apache.commons.io.FileUtils; |
| import org.apache.solr.SolrTestCaseJ4; |
| import org.apache.solr.SolrTestUtil; |
| import org.apache.solr.client.solrj.embedded.JettySolrRunner; |
| import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient; |
| import org.apache.solr.common.LinkedHashMapWriter; |
| import org.apache.solr.common.MapWriter; |
| import org.apache.solr.common.util.StrUtils; |
| import org.apache.solr.common.util.Utils; |
| import org.apache.solr.common.util.ValidatingJsonMap; |
| import org.apache.solr.handler.DumpRequestHandler; |
| import org.apache.solr.handler.TestSolrConfigHandlerConcurrent; |
| import org.apache.solr.request.SolrQueryRequest; |
| import org.apache.solr.response.SolrQueryResponse; |
| import org.apache.solr.search.SolrCache; |
| import org.apache.solr.util.RESTfulServerProvider; |
| import org.apache.solr.util.RestTestBase; |
| import org.apache.solr.util.RestTestHarness; |
| import org.apache.solr.util.SimplePostTool; |
| import org.eclipse.jetty.servlet.ServletHolder; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.noggit.JSONParser; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import static java.util.Arrays.asList; |
| import static org.apache.solr.common.util.Utils.getObjectByPath; |
| |
| public class TestSolrConfigHandler extends RestTestBase { |
| private static final int TIMEOUT_S = 10; |
| |
| private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); |
| |
| private static File tmpSolrHome; |
| private static File tmpConfDir; |
| |
| private static final String collection = "collection1"; |
| private static final String confDir = collection + "/conf"; |
| private JettySolrRunner jetty; |
| |
| public static ByteBuffer getFileContent(String f) throws IOException { |
| return getFileContent(f, true); |
| } |
| |
| /** |
| * @param loadFromClassPath if true, it will look in the classpath to find the file, |
| * otherwise load from absolute filesystem path. |
| */ |
| public static ByteBuffer getFileContent(String f, boolean loadFromClassPath) throws IOException { |
| ByteBuffer jar; |
| File file = loadFromClassPath ? SolrTestUtil.getFile(f) : new File(f); |
| try (FileInputStream fis = new FileInputStream(file)) { |
| byte[] buf = new byte[fis.available()]; |
| fis.read(buf); |
| jar = ByteBuffer.wrap(buf); |
| } |
| return jar; |
| } |
| |
| public static ByteBuffer persistZip(String loc, |
| @SuppressWarnings({"rawtypes"})Class... classes) throws IOException { |
| ByteBuffer jar = generateZip(classes); |
| try (FileOutputStream fos = new FileOutputStream(loc)){ |
| fos.write(jar.array(), 0, jar.limit()); |
| fos.flush(); |
| } |
| return jar; |
| } |
| |
| public static ByteBuffer generateZip(@SuppressWarnings({"rawtypes"})Class... classes) throws IOException { |
| SimplePostTool.BAOS bos = new SimplePostTool.BAOS(); |
| try (ZipOutputStream zipOut = new ZipOutputStream(bos)) { |
| zipOut.setLevel(ZipOutputStream.DEFLATED); |
| for (@SuppressWarnings({"rawtypes"})Class c : classes) { |
| String path = c.getName().replace('.', '/').concat(".class"); |
| ZipEntry entry = new ZipEntry(path); |
| ByteBuffer b = SimplePostTool.inputStreamToByteArray(c.getClassLoader().getResourceAsStream(path)); |
| zipOut.putNextEntry(entry); |
| zipOut.write(b.array(), 0, b.limit()); |
| zipOut.closeEntry(); |
| } |
| } |
| return bos.getByteBuffer(); |
| } |
| |
| |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| tmpSolrHome = SolrTestUtil.createTempDir().toFile(); |
| tmpConfDir = new File(tmpSolrHome, confDir); |
| FileUtils.copyDirectory(new File(SolrTestUtil.TEST_HOME()), tmpSolrHome.getAbsoluteFile()); |
| |
| final SortedMap<ServletHolder, String> extraServlets = new TreeMap<>(); |
| |
| System.setProperty("managed.schema.mutable", "true"); |
| System.setProperty("enable.update.log", "false"); |
| |
| jetty = createJettyAndHarness(tmpSolrHome.getAbsolutePath(), "solrconfig-managed-schema.xml", "schema-rest.xml", |
| "/solr", true, extraServlets); |
| // if (random().nextBoolean()) { |
| // log.info("These tests are run with V2 API"); |
| // restTestHarness.setServerProvider(() -> jetty.getBaseUrl().toString() + "/____v2/cores/" + DEFAULT_TEST_CORENAME); |
| // } |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| if (jetty != null) { |
| jetty.stop(); |
| } |
| super.tearDown(); |
| } |
| |
| public void testProperty() throws Exception { |
| RestTestHarness harness = restTestHarness; |
| MapWriter confMap = getRespMap("/config", harness); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/luke"), null)); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/system"), null)); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/mbeans"), null)); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/plugins"), null)); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/threads"), null)); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/properties"), null)); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/logging"), null)); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/file"), null)); |
| assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/ping"), null)); |
| |
| String payload = "{\n" + |
| " 'set-property' : { 'updateHandler.autoCommit.maxDocs':100, 'updateHandler.autoCommit.maxTime':10 , 'requestDispatcher.requestParsers.addHttpRequestToContext':true} \n" + |
| " }"; |
| runConfigCommand(harness, "/config", payload); |
| |
| |
| MapWriter m = getRespMap("/config/overlay", harness); |
| MapWriter props =null; |
| assertEquals("100", m._getStr("overlay/props/updateHandler/autoCommit/maxDocs", null)); |
| assertEquals("10", m._getStr("overlay/props/updateHandler/autoCommit/maxTime",null)); |
| |
| m = getRespMap("/config/updateHandler", harness); |
| assertNotNull(m._get("config/updateHandler/commitWithin/softCommit",null)); |
| assertNotNull(m._get("config/updateHandler/autoCommit/maxDocs",null)); |
| assertNotNull(m._get("config/updateHandler/autoCommit/maxTime",null)); |
| |
| m = getRespMap("/config", harness); |
| assertNotNull(m); |
| |
| assertEquals(m.toString(), "100", m._getStr("config/updateHandler/autoCommit/maxDocs",null)); |
| assertEquals("10", m._getStr("config/updateHandler/autoCommit/maxTime",null)); |
| assertEquals("true", m._getStr("config/requestDispatcher/requestParsers/addHttpRequestToContext",null)); |
| payload = "{\n" + |
| " 'unset-property' : 'updateHandler.autoCommit.maxDocs' \n" + |
| " }"; |
| runConfigCommand(harness, "/config", payload); |
| |
| m = getRespMap("/config/overlay", harness); |
| assertNull(m._get("overlay/props/updateHandler/autoCommit/maxDocs",null)); |
| assertEquals("10", m._getStr("overlay/props/updateHandler/autoCommit/maxTime",null)); |
| } |
| |
| @Ignore // MRM TODO: - debug, may not to poll a short while? |
| 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" + |
| " }"; |
| runConfigCommand(harness, "/config", payload); |
| |
| MapWriter m = getRespMap("/config/overlay", harness);//.get("overlay"); |
| assertEquals(m._get("overlay/userProps/my.custom.variable.a",null), "MODIFIEDA"); |
| assertEquals(m._get("overlay/userProps/my.custom.variable.b",null), "MODIFIEDB"); |
| |
| m = getRespMap("/dump?json.nl=map&initArgs=true", harness);//.get("initArgs"); |
| |
| assertEquals("MODIFIEDA", m._get("initArgs/defaults/a",null)); |
| assertEquals("MODIFIEDB", m._get("initArgs/defaults/b",null)); |
| |
| } |
| |
| public void testReqHandlerAPIs() throws Exception { |
| reqhandlertests(restTestHarness, null, null); |
| } |
| |
| public static Map runConfigCommand(RestTestHarness harness, String uri, String payload) throws IOException { |
| assertTrue(harness != null); |
| String json = SolrTestCaseJ4.json(payload); |
| log.info("going to send config command. path {} , payload: {}", uri, payload); |
| String response = harness.post(uri, json); |
| Map map = (Map) Utils.fromJSONString(response); |
| assertNull(response, map.get("errorMessages")); |
| assertNull(response, map.get("errors")); // Will this ever be returned? |
| return map; |
| } |
| |
| public static void runConfigCommandExpectFailure(RestTestHarness harness, String uri, String payload, String expectedErrorMessage) throws Exception { |
| String json = SolrTestCaseJ4.json(payload); |
| log.info("going to send config command. path {} , payload: {}", uri, payload); |
| String response = harness.post(uri, json); |
| Map map = (Map)Utils.fromJSONString(response); |
| assertNotNull(response, map.get("errorMessages")); |
| assertNotNull(response, map.get("error")); |
| assertTrue("Expected status != 0: " + response, 0L != (Long)((Map)map.get("responseHeader")).get("status")); |
| List errorDetails = (List)((Map)map.get("error")).get("details"); |
| List errorMessages = (List)((Map)errorDetails.get(0)).get("errorMessages"); |
| assertTrue("Expected '" + expectedErrorMessage + "': " + response, |
| errorMessages.get(0).toString().contains(expectedErrorMessage)); |
| } |
| |
| public static void reqhandlertests(RestTestHarness writeHarness, String testServerBaseUrl, CloudHttp2SolrClient cloudSolrClient) throws Exception { |
| String payload = "{\n" + |
| "'create-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' , 'startup' : 'lazy'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config/overlay", |
| cloudSolrClient, |
| asList("overlay", "requestHandler", "/x", "startup"), |
| "lazy", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'update-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' ,registerPath :'/solr,/v2', " + |
| " 'startup' : 'lazy' , 'a':'b' , 'defaults': {'def_a':'def A val', 'multival':['a','b','c']}}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config/overlay", |
| cloudSolrClient, |
| asList("overlay", "requestHandler", "/x", "a"), |
| "b", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'update-requesthandler' : { 'name' : '/dump', " + |
| "'initParams': 'a'," + |
| "'class': 'org.apache.solr.handler.DumpRequestHandler' ," + |
| " 'defaults': {'a':'A','b':'B','c':'C'}}\n" + |
| "}"; |
| |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config/overlay", |
| cloudSolrClient, |
| asList("overlay", "requestHandler", "/dump", "defaults", "c"), |
| "C", |
| TIMEOUT_S); |
| |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/x?getdefaults=true&json.nl=map", |
| cloudSolrClient, |
| asList("getdefaults", "def_a"), |
| "def A val", |
| TIMEOUT_S); |
| |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/x?param=multival&json.nl=map", |
| cloudSolrClient, |
| asList("params", "multival"), |
| asList("a", "b", "c"), |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'delete-requesthandler' : '/x'" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| boolean success = false; |
| long startTime = System.nanoTime(); |
| int maxTimeoutSeconds = 10; |
| while (TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) { |
| String uri = "/config/overlay"; |
| Map m = testServerBaseUrl == null ? getRespMap(uri, writeHarness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl + uri, cloudSolrClient); |
| if (null == Utils.getObjectByPath(m, true, asList("overlay", "requestHandler", "/x", "a"))) { |
| success = true; |
| break; |
| } |
| Thread.sleep(100); |
| |
| } |
| assertTrue("Could not delete requestHandler ", success); |
| |
| payload = "{\n" + |
| "'create-queryconverter' : { 'name' : 'qc', 'class': 'org.apache.solr.spelling.SpellingQueryConverter'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "queryConverter", "qc", "class"), |
| "org.apache.solr.spelling.SpellingQueryConverter", |
| TIMEOUT_S); |
| payload = "{\n" + |
| "'update-queryconverter' : { 'name' : 'qc', 'class': 'org.apache.solr.spelling.SuggestQueryConverter'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "queryConverter", "qc", "class"), |
| "org.apache.solr.spelling.SuggestQueryConverter", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'delete-queryconverter' : 'qc'" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "queryConverter", "qc"), |
| null, |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'create-searchcomponent' : { 'name' : 'tc', 'class': 'org.apache.solr.handler.component.TermsComponent'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "searchComponent", "tc", "class"), |
| "org.apache.solr.handler.component.TermsComponent", |
| TIMEOUT_S); |
| payload = "{\n" + |
| "'update-searchcomponent' : { 'name' : 'tc', 'class': 'org.apache.solr.handler.component.TermVectorComponent' }\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "searchComponent", "tc", "class"), |
| "org.apache.solr.handler.component.TermVectorComponent", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'delete-searchcomponent' : 'tc'" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "searchComponent", "tc"), |
| null, |
| TIMEOUT_S); |
| //<valueSourceParser name="countUsage" class="org.apache.solr.core.CountUsageValueSourceParser"/> |
| payload = "{\n" + |
| "'create-valuesourceparser' : { 'name' : 'cu', 'class': 'org.apache.solr.core.CountUsageValueSourceParser'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "valueSourceParser", "cu", "class"), |
| "org.apache.solr.core.CountUsageValueSourceParser", |
| TIMEOUT_S); |
| // <valueSourceParser name="nvl" class="org.apache.solr.search.function.NvlValueSourceParser"> |
| // <float name="nvlFloatValue">0.0</float> |
| // </valueSourceParser> |
| payload = "{\n" + |
| "'update-valuesourceparser' : { 'name' : 'cu', 'class': 'org.apache.solr.search.function.NvlValueSourceParser'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "valueSourceParser", "cu", "class"), |
| "org.apache.solr.search.function.NvlValueSourceParser", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'delete-valuesourceparser' : 'cu'" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "valueSourceParser", "cu"), |
| null, |
| TIMEOUT_S); |
| // <transformer name="mytrans2" class="org.apache.solr.response.transform.ValueAugmenterFactory" > |
| // <int name="value">5</int> |
| // </transformer> |
| payload = "{\n" + |
| "'create-transformer' : { 'name' : 'mytrans', 'class': 'org.apache.solr.response.transform.ValueAugmenterFactory', 'value':'5'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "transformer", "mytrans", "class"), |
| "org.apache.solr.response.transform.ValueAugmenterFactory", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'update-transformer' : { 'name' : 'mytrans', 'class': 'org.apache.solr.response.transform.ValueAugmenterFactory', 'value':'6'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "transformer", "mytrans", "value"), |
| "6", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'delete-transformer' : 'mytrans'," + |
| "'create-initparams' : { 'name' : 'hello', 'key':'val'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| Map map = testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "transformer", "mytrans"), |
| null, |
| TIMEOUT_S); |
| |
| List l = (List) Utils.getObjectByPath(map, false, asList("config", "initParams")); |
| assertNotNull("no object /config/initParams : "+ map , l); |
| assertEquals( 2, l.size()); |
| assertEquals( "val", ((Map)l.get(1)).get("key") ); |
| |
| |
| payload = "{\n" + |
| " 'add-searchcomponent': {\n" + |
| " 'name': 'myspellcheck',\n" + |
| " 'class': 'solr.SpellCheckComponent',\n" + |
| " 'queryAnalyzerFieldType': 'text_general',\n" + |
| " 'spellchecker': {\n" + |
| " 'name': 'default',\n" + |
| " 'field': '_text_',\n" + |
| " 'class': 'solr.DirectSolrSpellChecker'\n" + |
| " }\n" + |
| " }\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| map = testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "searchComponent", "myspellcheck", "spellchecker", "class"), |
| "solr.DirectSolrSpellChecker", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| " 'add-requesthandler': {\n" + |
| " name : '/dump100',\n" + |
| " registerPath :'/solr,/v2',"+ |
| " class : 'org.apache.solr.handler.DumpRequestHandler'," + |
| " suggester: [{name: s1,lookupImpl: FuzzyLookupFactory, dictionaryImpl : DocumentDictionaryFactory}," + |
| " {name: s2,lookupImpl: FuzzyLookupFactory , dictionaryImpl : DocumentExpressionDictionaryFactory}]" + |
| " }\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| map = testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config", |
| cloudSolrClient, |
| asList("config", "requestHandler", "/dump100", "class"), |
| "org.apache.solr.handler.DumpRequestHandler", |
| TIMEOUT_S); |
| |
| map = getRespMap("/dump100?json.nl=arrmap&initArgs=true", writeHarness); |
| List initArgs = (List) map.get("initArgs"); |
| assertNotNull(initArgs); |
| assertTrue(initArgs.size() >= 2); |
| assertTrue(((Map)initArgs.get(2)).containsKey("suggester")); |
| assertTrue(((Map)initArgs.get(1)).containsKey("suggester")); |
| |
| payload = "{\n" + |
| "'add-requesthandler' : { 'name' : '/dump101', 'class': " + |
| "'" + CacheTest.class.getName() + "', " + |
| " registerPath :'/solr,/v2'"+ |
| ", 'startup' : 'lazy'}\n" + |
| "}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| |
| testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config/overlay", |
| cloudSolrClient, |
| asList("overlay", "requestHandler", "/dump101", "startup"), |
| "lazy", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'add-cache' : {name:'lfuCacheDecayFalse', class:'solr.CaffeineCache', size:10 ,initialSize:9 , timeDecay:false }," + |
| "'add-cache' : {name: 'perSegFilter', class: 'solr.search.CaffeineCache', size:10, initialSize:0 , autowarmCount:10}}"; |
| runConfigCommand(writeHarness, "/config", payload); |
| |
| map = testForResponseElement(writeHarness, |
| testServerBaseUrl, |
| "/config/overlay", |
| cloudSolrClient, |
| asList("overlay", "cache", "lfuCacheDecayFalse", "class"), |
| "solr.CaffeineCache", |
| TIMEOUT_S); |
| assertEquals("solr.search.CaffeineCache",getObjectByPath(map, true, ImmutableList.of("overlay", "cache", "perSegFilter", "class"))); |
| |
| map = getRespMap("/dump101?cacheNames=lfuCacheDecayFalse&cacheNames=perSegFilter", writeHarness); |
| assertEquals("Actual output "+ Utils.toJSONString(map), "org.apache.solr.search.CaffeineCache",getObjectByPath(map, true, ImmutableList.of( "caches", "perSegFilter"))); |
| assertEquals("Actual output "+ Utils.toJSONString(map), "org.apache.solr.search.CaffeineCache",getObjectByPath(map, true, ImmutableList.of( "caches", "lfuCacheDecayFalse"))); |
| |
| } |
| |
| public void testFailures() throws Exception { |
| String payload = "{ not-a-real-command: { param1: value1, param2: value2 } }"; |
| runConfigCommandExpectFailure(restTestHarness, "/config", payload, "Unknown operation 'not-a-real-command'"); |
| |
| payload = "{ set-property: { update.autoCreateFields: false } }"; |
| runConfigCommandExpectFailure(restTestHarness, "/config", payload, "'update.autoCreateFields' is not an editable property"); |
| |
| payload = "{ set-property: { updateHandler.autoCommit.maxDocs: false } }"; |
| runConfigCommandExpectFailure(restTestHarness, "/config", payload, "Property updateHandler.autoCommit.maxDocs must be of Integer type"); |
| |
| payload = "{ unset-property: not-an-editable-property }"; |
| runConfigCommandExpectFailure(restTestHarness, "/config", payload, "'[not-an-editable-property]' is not an editable property"); |
| |
| for (String component : new String[] { |
| "requesthandler", "searchcomponent", "initparams", "queryresponsewriter", "queryparser", |
| "valuesourceparser", "transformer", "updateprocessor", "queryconverter", "listener"}) { |
| for (String operation : new String[] { "add", "update" }) { |
| payload = "{ " + operation + "-" + component + ": { param1: value1 } }"; |
| runConfigCommandExpectFailure(restTestHarness, "/config", payload, "'name' is a required field"); |
| } |
| payload = "{ delete-" + component + ": not-a-real-component-name }"; |
| runConfigCommandExpectFailure(restTestHarness, "/config", payload, "NO such "); |
| } |
| } |
| |
| public static class CacheTest extends DumpRequestHandler { |
| @Override |
| public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { |
| super.handleRequestBody(req, rsp); |
| String[] caches = req.getParams().getParams("cacheNames"); |
| if(caches != null && caches.length>0){ |
| HashMap m = new HashMap(); |
| rsp.add("caches", m); |
| for (String c : caches) { |
| SolrCache cache = req.getSearcher().getCache(c); |
| if(cache != null) m.put(c, cache.getClass().getName()); |
| } |
| } |
| } |
| } |
| |
| public static LinkedHashMapWriter testForResponseElement(RestTestHarness harness, |
| String testServerBaseUrl, |
| String uri, |
| CloudHttp2SolrClient cloudSolrClient, List<String> jsonPath, |
| Object expected, |
| long maxTimeoutSeconds) throws Exception { |
| |
| boolean success = false; |
| long startTime = System.nanoTime(); |
| LinkedHashMapWriter m = null; |
| |
| while (TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) { |
| try { |
| m = testServerBaseUrl == null ? getRespMap(uri, harness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl + uri, cloudSolrClient); |
| log.info("response is {}", m); |
| } catch (Exception e) { |
| continue; |
| |
| } |
| Object actual = Utils.getObjectByPath(m, false, jsonPath); |
| |
| if (expected instanceof ValidatingJsonMap.PredicateWithErrMsg) { |
| ValidatingJsonMap.PredicateWithErrMsg predicate = (ValidatingJsonMap.PredicateWithErrMsg) expected; |
| if (predicate.test(actual) == null) { |
| success = true; |
| break; |
| } |
| |
| } else { |
| if (Objects.equals(expected, actual)) { |
| success = true; |
| break; |
| } |
| } |
| } |
| assertTrue(StrUtils.formatString("Could not get expected value ''{0}'' for path ''{1}'' full output: {2}, from server: {3}", expected, StrUtils.join(jsonPath, '/'), m.toString(), testServerBaseUrl), success); |
| |
| return m; |
| } |
| |
| @Ignore //MRM TODO: debug |
| public void testReqParams() throws Exception { |
| RestTestHarness harness = restTestHarness; |
| String payload = " {\n" + |
| " 'set' : {'x': {" + |
| " 'a':'A val',\n" + |
| " 'b': 'B val'}\n" + |
| " }\n" + |
| " }"; |
| |
| |
| TestSolrConfigHandler.runConfigCommand(harness, "/config/params", payload); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/params", |
| null, |
| asList("response", "params", "x", "a"), |
| "A val", |
| TIMEOUT_S); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/params", |
| null, |
| asList("response", "params", "x", "b"), |
| "B val", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'create-requesthandler' : { 'name' : '/d', registerPath :'/solr,/v2' , 'class': 'org.apache.solr.handler.DumpRequestHandler' }\n" + |
| "}"; |
| |
| TestSolrConfigHandler.runConfigCommand(harness, "/config", payload); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/overlay", |
| null, |
| asList("overlay", "requestHandler", "/d", "name"), |
| "/d", |
| TIMEOUT_S); |
| |
| TestSolrConfigHandler.testForResponseElement(harness, |
| null, |
| "/d?useParams=x", |
| null, |
| asList("params", "a"), |
| "A val", |
| TIMEOUT_S); |
| TestSolrConfigHandler.testForResponseElement(harness, |
| null, |
| "/d?useParams=x&a=fomrequest", |
| null, |
| asList("params", "a"), |
| "fomrequest", |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| "'create-requesthandler' : { 'name' : '/dump1', registerPath :'/solr,/v2' , 'class': 'org.apache.solr.handler.DumpRequestHandler', 'useParams':'x' }\n" + |
| "}"; |
| |
| TestSolrConfigHandler.runConfigCommand(harness, "/config", payload); |
| |
| TestSolrConfigHandler.testForResponseElement(harness, |
| null, |
| "/config/overlay", |
| null, |
| asList("overlay", "requestHandler", "/dump1", "name"), |
| "/dump1", |
| TIMEOUT_S); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/dump1", |
| null, |
| asList("params", "a"), |
| "A val", |
| TIMEOUT_S); |
| |
| |
| payload = " {\n" + |
| " 'set' : {'y':{\n" + |
| " 'c':'CY val',\n" + |
| " 'b': 'BY val', " + |
| " 'd': ['val 1', 'val 2']}\n" + |
| " }\n" + |
| " }"; |
| |
| |
| TestSolrConfigHandler.runConfigCommand(harness, "/config/params", payload); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/params", |
| null, |
| asList("response", "params", "y", "c"), |
| "CY val", |
| TIMEOUT_S); |
| |
| TestSolrConfigHandler.testForResponseElement(harness, |
| null, |
| "/dump1?useParams=y", |
| null, |
| asList("params", "c"), |
| "CY val", |
| TIMEOUT_S); |
| |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/dump1?useParams=y", |
| null, |
| asList("params", "b"), |
| "BY val", |
| TIMEOUT_S); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/dump1?useParams=y", |
| null, |
| asList("params", "a"), |
| "A val", |
| TIMEOUT_S); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/dump1?useParams=y", |
| null, |
| asList("params", "d"), |
| asList("val 1", "val 2"), |
| TIMEOUT_S); |
| |
| payload = " {\n" + |
| " 'update' : {'y': {\n" + |
| " 'c':'CY val modified',\n" + |
| " 'e':'EY val',\n" + |
| " 'b': 'BY val'" + |
| "}\n" + |
| " }\n" + |
| " }"; |
| |
| |
| TestSolrConfigHandler.runConfigCommand(harness, "/config/params", payload); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/params", |
| null, |
| asList("response", "params", "y", "c"), |
| "CY val modified", |
| TIMEOUT_S); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/params", |
| null, |
| asList("response", "params", "y", "e"), |
| "EY val", |
| TIMEOUT_S); |
| |
| payload = " {\n" + |
| " 'set' : {'y': {\n" + |
| " 'p':'P val',\n" + |
| " 'q': 'Q val'" + |
| "}\n" + |
| " }\n" + |
| " }"; |
| |
| |
| TestSolrConfigHandler.runConfigCommand(harness, "/config/params", payload); |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/params", |
| null, |
| asList("response", "params", "y", "p"), |
| "P val", |
| TIMEOUT_S); |
| |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/params", |
| null, |
| asList("response", "params", "y", "c"), |
| null, |
| TIMEOUT_S); |
| payload = " {'delete' : 'y'}"; |
| TestSolrConfigHandler.runConfigCommand(harness, "/config/params", payload); |
| TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/config/params", |
| null, |
| asList("response", "params", "y", "p"), |
| null, |
| TIMEOUT_S); |
| |
| payload = "{\n" + |
| " 'create-requesthandler': {\n" + |
| " 'name': 'aRequestHandler',\n" + |
| " 'registerPath': '/v2',\n" + |
| " 'class': 'org.apache.solr.handler.DumpRequestHandler',\n" + |
| " 'spec': {\n" + |
| " 'methods': [\n" + |
| " 'GET',\n" + |
| " 'POST'\n" + |
| " ],\n" + |
| " 'url': {\n" + |
| " 'paths': [\n" + |
| " '/something/{part1}/fixed/{part2}'\n" + |
| " ]\n" + |
| " }\n" + |
| " }\n" + |
| " }\n" + |
| "}"; |
| |
| TestSolrConfigHandler.runConfigCommand(harness, "/config", payload); |
| TestSolrConfigHandler.testForResponseElement(harness, |
| null, |
| "/config/overlay", |
| null, |
| asList("overlay", "requestHandler", "aRequestHandler", "class"), |
| "org.apache.solr.handler.DumpRequestHandler", |
| TIMEOUT_S); |
| RESTfulServerProvider oldProvider = restTestHarness.getServerProvider(); |
| restTestHarness.setServerProvider(() -> jetty.getBaseUrl().toString() + "/____v2/cores/" + DEFAULT_TEST_CORENAME); |
| |
| Map rsp = TestSolrConfigHandler.testForResponseElement( |
| harness, |
| null, |
| "/something/part1_Value/fixed/part2_Value?urlTemplateValues=part1&urlTemplateValues=part2", |
| null, |
| asList("urlTemplateValues"), |
| new ValidatingJsonMap.PredicateWithErrMsg() { |
| @Override |
| public String test(Object o) { |
| if (o instanceof Map) { |
| Map m = (Map) o; |
| if ("part1_Value".equals(m.get("part1")) && "part2_Value".equals(m.get("part2"))) return null; |
| |
| } |
| return "no match"; |
| } |
| |
| @Override |
| public String toString() { |
| return "{part1:part1_Value, part2 : part2_Value]"; |
| } |
| }, |
| TIMEOUT_S); |
| restTestHarness.setServerProvider(oldProvider); |
| |
| } |
| |
| |
| public static LinkedHashMapWriter getRespMap(String path, RestTestHarness restHarness) throws Exception { |
| String response = restHarness.query(path); |
| try { |
| return (LinkedHashMapWriter) Utils.MAPWRITEROBJBUILDER.apply(Utils.getJSONParser(new StringReader(response))).getVal(); |
| } catch (JSONParser.ParseException e) { |
| log.error(response); |
| return new LinkedHashMapWriter(); |
| } |
| } |
| } |