From 8596dfc1e2f54c4930a2a4ff67ff4dd1b533f1b3 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Fri, 8 Nov 2024 19:54:19 -0500 Subject: [PATCH 1/7] SOLR-14680: Remove SimpleMap (affects NamedList) and the rest of SolrJ org.apache.solr.cluster.api NamedList: * new getOrDefault * remove forEachEntry, forEachKey, abortableForEachKey, abortableForEach * remove asMap, get(key, default) --- .../src/java/org/apache/solr/cli/ApiTool.java | 2 +- .../java/org/apache/solr/cli/CreateTool.java | 10 +- .../java/org/apache/solr/cli/DeleteTool.java | 2 +- .../src/java/org/apache/solr/cli/SolrCLI.java | 2 +- .../java/org/apache/solr/cli/StatusTool.java | 5 +- .../apache/solr/core/OverlaidConfigNode.java | 4 +- .../java/org/apache/solr/core/PluginInfo.java | 2 +- .../org/apache/solr/core/SolrXmlConfig.java | 7 +- .../org/apache/solr/schema/IndexSchema.java | 2 +- .../org/apache/solr/search/CacheConfig.java | 4 +- .../org/apache/solr/update/UpdateLog.java | 3 +- .../org/apache/solr/util/DOMConfigNode.java | 11 +- .../org/apache/solr/util/DataConfigNode.java | 112 +++-- .../solr/cloud/TestLazySolrCluster.java | 104 ---- .../apache/solr/core/TestCodecSupport.java | 3 +- .../handler/RequestHandlerMetricsTest.java | 7 +- .../admin/CoreAdminHandlerActionTest.java | 5 +- .../apache/solr/common/LazySolrCluster.java | 465 ------------------ .../org/apache/solr/common/SimpleZkMap.java | 136 ----- .../common/cloud/PerReplicaStatesOps.java | 2 +- .../org/apache/solr/cluster/api/ApiType.java | 29 -- .../solr/cluster/api/CollectionConfig.java | 25 - .../apache/solr/cluster/api/HashRange.java | 38 -- .../org/apache/solr/cluster/api/Resource.java | 41 -- .../org/apache/solr/cluster/api/Router.java | 25 - .../org/apache/solr/cluster/api/Shard.java | 37 -- .../apache/solr/cluster/api/ShardReplica.java | 57 --- .../apache/solr/cluster/api/SimpleMap.java | 102 ---- .../apache/solr/cluster/api/SolrCluster.java | 45 -- .../solr/cluster/api/SolrCollection.java | 33 -- .../org/apache/solr/cluster/api/SolrNode.java | 34 -- .../apache/solr/cluster/api/package-info.java | 19 - .../org/apache/solr/common/ConfigNode.java | 9 +- .../apache/solr/common/cloud/DocRouter.java | 6 +- .../solr/common/cloud/PerReplicaStates.java | 26 +- .../org/apache/solr/common/util/DOMUtil.java | 2 +- .../apache/solr/common/util/NamedList.java | 45 +- .../solr/common/util/WrappedSimpleMap.java | 57 --- .../PerReplicaStatesIntegrationTest.java | 6 +- 39 files changed, 140 insertions(+), 1384 deletions(-) delete mode 100644 solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java delete mode 100644 solr/solrj-zookeeper/src/java/org/apache/solr/common/LazySolrCluster.java delete mode 100644 solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/CollectionConfig.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/Router.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/SolrCollection.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java delete mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java delete mode 100644 solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java diff --git a/solr/core/src/java/org/apache/solr/cli/ApiTool.java b/solr/core/src/java/org/apache/solr/cli/ApiTool.java index 02f893b3753..c1887cfbc14 100644 --- a/solr/core/src/java/org/apache/solr/cli/ApiTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ApiTool.java @@ -98,7 +98,7 @@ protected String callGet(String url, String credentials) throws Exception { NamedList response = solrClient.request(req); // pretty-print the response to stdout CharArr arr = new CharArr(); - new JSONWriter(arr, 2).write(response.asMap()); + new JSONWriter(arr, 2).write(response.asShallowMap()); return arr.toString(); } } diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java index 5fc57501d93..071b37dc6af 100644 --- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java @@ -22,7 +22,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.commons.cli.CommandLine; @@ -167,10 +166,9 @@ protected void createCore(CommandLine cli, SolrClient solrClient) throws Excepti String coreRootDirectory; // usually same as solr home, but not always - Map systemInfo = - solrClient - .request(new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)) - .asMap(); + NamedList systemInfo = + solrClient.request( + new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); // convert raw JSON into user-friendly output coreRootDirectory = (String) systemInfo.get("core_root"); @@ -321,7 +319,7 @@ protected void createCollection(CloudSolrClient cloudSolrClient, CommandLine cli if (isVerbose()) { // pretty-print the response to stdout CharArr arr = new CharArr(); - new JSONWriter(arr, 2).write(response.asMap()); + new JSONWriter(arr, 2).write(response.asShallowMap()); echo(arr.toString()); } String endMessage = diff --git a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java index 2c42f8dc030..7a32026291b 100644 --- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java +++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java @@ -211,7 +211,7 @@ protected void deleteCollection(CloudSolrClient cloudSolrClient, CommandLine cli if (isVerbose() && response != null) { // pretty-print the response to stdout CharArr arr = new CharArr(); - new JSONWriter(arr, 2).write(response.asMap()); + new JSONWriter(arr, 2).write(response.asShallowMap()); echo(arr.toString()); echo("\n"); } diff --git a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java index 00a97b4434c..de9ccaa1fac 100755 --- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java +++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java @@ -705,7 +705,7 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin Map failureStatus = (Map) existsCheckResult.get("initFailures"); String errorMsg = (String) failureStatus.get(coreName); - final boolean hasName = coreStatus != null && coreStatus.asMap().containsKey(NAME); + final boolean hasName = coreStatus != null && coreStatus.get(NAME) != null; exists = hasName || errorMsg != null; wait = hasName && errorMsg == null && "true".equals(coreStatus.get("isLoading")); } while (wait && System.nanoTime() - startWaitAt < MAX_WAIT_FOR_CORE_LOAD_NANOS); diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java b/solr/core/src/java/org/apache/solr/cli/StatusTool.java index dcf0200e511..cd36cfa279a 100644 --- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java +++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java @@ -358,13 +358,14 @@ protected Map getCloudStatus(SolrClient solrClient, String zkHos Map cloudStatus = new LinkedHashMap<>(); cloudStatus.put("ZooKeeper", (zkHost != null) ? zkHost : "?"); + // TODO add booleans to request just what we want; not everything NamedList json = solrClient.request(new CollectionAdminRequest.ClusterStatus()); List liveNodes = (List) json.findRecursive("cluster", "live_nodes"); cloudStatus.put("liveNodes", String.valueOf(liveNodes.size())); - Map collections = - ((NamedList) json.findRecursive("cluster", "collections")).asMap(); + // TODO get this as a metric from the metrics API instead, or something else. + var collections = (NamedList) json.findRecursive("cluster", "collections"); cloudStatus.put("collections", String.valueOf(collections.size())); return cloudStatus; diff --git a/solr/core/src/java/org/apache/solr/core/OverlaidConfigNode.java b/solr/core/src/java/org/apache/solr/core/OverlaidConfigNode.java index 30561311dfc..c6fab13fef7 100644 --- a/solr/core/src/java/org/apache/solr/core/OverlaidConfigNode.java +++ b/solr/core/src/java/org/apache/solr/core/OverlaidConfigNode.java @@ -19,9 +19,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.function.Function; import java.util.function.Predicate; -import org.apache.solr.cluster.api.SimpleMap; import org.apache.solr.common.ConfigNode; /** A config node impl which has an overlay */ @@ -80,7 +80,7 @@ public String name() { } @Override - public SimpleMap attributes() { + public Map attributes() { return delegate.attributes(); } 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 5c24ee9a8f4..b55907f1e2f 100644 --- a/solr/core/src/java/org/apache/solr/core/PluginInfo.java +++ b/solr/core/src/java/org/apache/solr/core/PluginInfo.java @@ -118,7 +118,7 @@ public PluginInfo(ConfigNode node, String err, boolean requireName, boolean requ className = cName.className; pkgName = cName.pkg; initArgs = DOMUtil.childNodesToNamedList(node); - attributes = node.attributes().asMap(); + attributes = node.attributes(); children = loadSubPlugins(node); isFromSolrConfig = true; } diff --git a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java index b19d65320ad..0057da2f505 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java +++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.function.Consumer; @@ -131,10 +132,10 @@ public static NodeConfig fromConfig( // since it is arranged as a separate section it is placed here Map coreAdminHandlerActions = readNodeListAsNamedList(root.get("coreAdminHandlerActions"), "") - .asMap() + .asShallowMap() .entrySet() .stream() - .collect(Collectors.toMap(item -> item.getKey(), item -> item.getValue().toString())); + .collect(Collectors.toMap(Entry::getKey, item -> item.getValue().toString())); UpdateShardHandlerConfig updateConfig; if (deprecatedUpdateConfig == null) { @@ -733,7 +734,7 @@ private static MetricsConfig getMetricsConfig(ConfigNode metrics) { ConfigNode caching = metrics.get("solr/metrics/caching"); if (caching != null) { Object threadsCachingIntervalSeconds = - DOMUtil.childNodesToNamedList(caching).get("threadsIntervalSeconds", null); + DOMUtil.childNodesToNamedList(caching).get("threadsIntervalSeconds"); builder.setCacheConfig( new MetricsConfig.CacheConfig( threadsCachingIntervalSeconds == null diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java index 06dad3175dc..b3f59eab4e7 100644 --- a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java +++ b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java @@ -526,7 +526,7 @@ protected void readSchema(ConfigSetService.ConfigResource is) { log.info("{}", sb); } - version = Float.parseFloat(rootNode.attributes().get("version", "1.0f")); + version = Float.parseFloat(rootNode.attributes().getOrDefault("version", "1.0f")); // load the Field Types final FieldTypePluginLoader typeLoader = diff --git a/solr/core/src/java/org/apache/solr/search/CacheConfig.java b/solr/core/src/java/org/apache/solr/search/CacheConfig.java index 83ce72af836..da05152a86d 100644 --- a/solr/core/src/java/org/apache/solr/search/CacheConfig.java +++ b/solr/core/src/java/org/apache/solr/search/CacheConfig.java @@ -87,7 +87,7 @@ public static Map getMultipleConfigs( for (ConfigNode node : nodes) { if (node.boolAttr("enabled", true)) { CacheConfig config = - getConfig(loader, solrConfig, node.name(), node.attributes().asMap(), configPath); + getConfig(loader, solrConfig, node.name(), node.attributes(), configPath); result.put(config.args.get(NAME), config); } } @@ -98,7 +98,7 @@ public static CacheConfig getConfig(SolrConfig solrConfig, ConfigNode node, Stri if (!node.boolAttr("enabled", true) || !node.exists()) { return null; } - return getConfig(solrConfig, node.name(), node.attributes().asMap(), xpath); + return getConfig(solrConfig, node.name(), node.attributes(), xpath); } public static CacheConfig getConfig( diff --git a/solr/core/src/java/org/apache/solr/update/UpdateLog.java b/solr/core/src/java/org/apache/solr/update/UpdateLog.java index d1c430bceee..4d148ed0af4 100644 --- a/solr/core/src/java/org/apache/solr/update/UpdateLog.java +++ b/solr/core/src/java/org/apache/solr/update/UpdateLog.java @@ -391,7 +391,8 @@ public void init(PluginInfo info) { } int timeoutMs = objToInt( - info.initArgs.get("docLockTimeoutMs", info.initArgs.get("versionBucketLockTimeoutMs")), + info.initArgs.getOrDefault( + "docLockTimeoutMs", info.initArgs.get("versionBucketLockTimeoutMs")), EnvUtils.getPropertyAsLong("solr.update.docLockTimeoutMs", 0L).intValue()); updateLocks = new UpdateLocks(timeoutMs); diff --git a/solr/core/src/java/org/apache/solr/util/DOMConfigNode.java b/solr/core/src/java/org/apache/solr/util/DOMConfigNode.java index f7a47b5cc93..d35cd4bccc5 100644 --- a/solr/core/src/java/org/apache/solr/util/DOMConfigNode.java +++ b/solr/core/src/java/org/apache/solr/util/DOMConfigNode.java @@ -18,14 +18,11 @@ package org.apache.solr.util; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Function; -import org.apache.solr.cluster.api.SimpleMap; import org.apache.solr.common.ConfigNode; import org.apache.solr.common.util.DOMUtil; -import org.apache.solr.common.util.WrappedSimpleMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -33,7 +30,7 @@ public class DOMConfigNode implements ConfigNode { private final Node node; - SimpleMap attrs; + Map attrs; @Override public String name() { @@ -50,10 +47,10 @@ public DOMConfigNode(Node node) { } @Override - public SimpleMap attributes() { + public Map attributes() { if (attrs != null) return attrs; Map attrs = DOMUtil.toMap(node.getAttributes()); - return this.attrs = attrs.size() == 0 ? EMPTY : new WrappedSimpleMap<>(attrs); + return this.attrs = attrs.isEmpty() ? Map.of() : attrs; } @Override @@ -85,6 +82,4 @@ public void forEachChild(Function fun) { if (Boolean.FALSE.equals(toContinue)) break; } } - - private static final SimpleMap EMPTY = new WrappedSimpleMap<>(Collections.emptyMap()); } diff --git a/solr/core/src/java/org/apache/solr/util/DataConfigNode.java b/solr/core/src/java/org/apache/solr/util/DataConfigNode.java index e8a00075031..fde5693534a 100644 --- a/solr/core/src/java/org/apache/solr/util/DataConfigNode.java +++ b/solr/core/src/java/org/apache/solr/util/DataConfigNode.java @@ -17,31 +17,30 @@ package org.apache.solr.util; +import java.util.AbstractMap; +import java.util.AbstractSet; import java.util.ArrayList; -import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; -import org.apache.solr.cluster.api.SimpleMap; import org.apache.solr.common.ConfigNode; import org.apache.solr.common.util.PropertiesUtil; -import org.apache.solr.common.util.WrappedSimpleMap; /** ConfigNode impl that copies and maintains data internally from DOM */ public class DataConfigNode implements ConfigNode { public final String name; - public final SimpleMap attributes; - public final SimpleMap> kids; + public final Map attributes; + public final Map> kids; public final String textData; public DataConfigNode(ConfigNode root) { Map> kids = new LinkedHashMap<>(); name = root.name(); - attributes = wrap(root.attributes()); + attributes = wrapSubstituting(root.attributes()); textData = root.txt(); root.forEachChild( it -> { @@ -54,31 +53,17 @@ public DataConfigNode(ConfigNode root) { e.setValue(List.copyOf(e.getValue())); } } - this.kids = kids.isEmpty() ? EMPTY : new WrappedSimpleMap<>(Map.copyOf(kids)); + this.kids = Map.copyOf(kids); } - public String subtituteVal(String s) { + private static String substituteVal(String s) { return PropertiesUtil.substitute(s, SUBSTITUTES.get()); } - private SimpleMap wrap(SimpleMap delegate) { + /** provides a substitute view, and read-only */ + private static Map wrapSubstituting(Map delegate) { if (delegate.size() == 0) return delegate; // avoid unnecessary object creation - return new SimpleMap<>() { - @Override - public String get(String key) { - return subtituteVal(delegate.get(key)); - } - - @Override - public void forEachEntry(BiConsumer fun) { - delegate.forEachEntry((k, v) -> fun.accept(k, subtituteVal(v))); - } - - @Override - public int size() { - return delegate.size(); - } - }; + return new SubstitutingMap(delegate); } @Override @@ -88,11 +73,11 @@ public String name() { @Override public String txt() { - return subtituteVal(textData); + return substituteVal(textData); } @Override - public SimpleMap attributes() { + public Map attributes() { return attributes; } @@ -104,7 +89,7 @@ public ConfigNode child(String name) { @Override public List getAll(String name) { - return kids.get(name, Collections.emptyList()); + return kids.getOrDefault(name, List.of()); } @Override @@ -126,7 +111,7 @@ public List getAll(Predicate test, Set matchName @Override public void forEachChild(Function fun) { - kids.forEachEntry( + kids.forEach( (s, configNodes) -> { if (configNodes != null) { configNodes.forEach(fun::apply); @@ -134,6 +119,69 @@ public void forEachChild(Function fun) { }); } - public static final SimpleMap> EMPTY = - new WrappedSimpleMap<>(Collections.emptyMap()); + private static class SubstitutingMap extends AbstractMap { + + private final Map delegate; + + SubstitutingMap(Map delegate) { + this.delegate = delegate; + } + + @Override + public String get(Object key) { + return substituteVal(delegate.get(key)); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public Set keySet() { + return delegate.keySet(); + } + + @Override + public Set> entrySet() { + return new AbstractSet<>() { + @Override + public Iterator> iterator() { + // using delegate, return an iterator using Streams + return delegate.entrySet().stream() + .map(entry -> (Entry) new SubstitutingEntry(entry)) + .iterator(); + } + + @Override + public int size() { + return delegate.size(); + } + }; + } + + private static class SubstitutingEntry implements Entry { + + private final Entry delegateEntry; + + SubstitutingEntry(Entry delegateEntry) { + this.delegateEntry = delegateEntry; + } + + @Override + public String getKey() { + return delegateEntry.getKey(); + } + + @Override + public String getValue() { + return substituteVal(delegateEntry.getValue()); + } + + @Override + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + } + } } diff --git a/solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java b/solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java deleted file mode 100644 index 7d7f7024953..00000000000 --- a/solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.cloud; - -import java.util.ArrayList; -import java.util.List; -import org.apache.solr.client.solrj.impl.CloudSolrClient; -import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.cluster.api.CollectionConfig; -import org.apache.solr.cluster.api.SimpleMap; -import org.apache.solr.cluster.api.SolrCollection; -import org.apache.solr.common.LazySolrCluster; -import org.apache.solr.common.cloud.SolrZkClient; -import org.apache.solr.common.cloud.ZkStateReader; -import org.apache.zookeeper.CreateMode; -import org.junit.BeforeClass; - -public class TestLazySolrCluster extends SolrCloudTestCase { - @BeforeClass - public static void setupCluster() throws Exception { - configureCluster(5) - .addConfig( - "conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf")) - .configure(); - } - - public void test() throws Exception { - CloudSolrClient cloudClient = cluster.getSolrClient(); - String collection = "testLazyCluster1"; - cloudClient.request(CollectionAdminRequest.createCollection(collection, "conf1", 2, 2)); - cluster.waitForActiveCollection(collection, 2, 4); - collection = "testLazyCluster2"; - cloudClient.request(CollectionAdminRequest.createCollection(collection, "conf1", 2, 2)); - cluster.waitForActiveCollection(collection, 2, 4); - - LazySolrCluster solrCluster = new LazySolrCluster(ZkStateReader.from(cloudClient)); - SimpleMap colls = solrCluster.collections(); - - SolrCollection c = colls.get("testLazyCluster1"); - assertNotNull(c); - c = colls.get("testLazyCluster2"); - assertNotNull(c); - int[] count = new int[1]; - solrCluster.collections().forEachEntry((s, solrCollection) -> count[0]++); - assertEquals(2, count[0]); - - count[0] = 0; - - assertEquals(2, solrCluster.collections().get("testLazyCluster1").shards().size()); - solrCluster - .collections() - .get("testLazyCluster1") - .shards() - .forEachEntry((s, shard) -> shard.replicas().forEachEntry((s1, replica) -> count[0]++)); - assertEquals(4, count[0]); - - assertEquals(5, solrCluster.nodes().size()); - SolrZkClient zkClient = ZkStateReader.from(cloudClient).getZkClient(); - zkClient.create(ZkStateReader.CONFIGS_ZKNODE + "/conf1/a", null, CreateMode.PERSISTENT, true); - zkClient.create( - ZkStateReader.CONFIGS_ZKNODE + "/conf1/a/aa1", new byte[1024], CreateMode.PERSISTENT, true); - zkClient.create( - ZkStateReader.CONFIGS_ZKNODE + "/conf1/a/aa2", - new byte[1024 * 2], - CreateMode.PERSISTENT, - true); - - List allFiles = new ArrayList<>(); - byte[] buf = new byte[3 * 1024]; - CollectionConfig conf1 = solrCluster.configs().get("conf1"); - conf1 - .resources() - .abortableForEach( - (s, resource) -> { - allFiles.add(s); - if ("a/aa1".equals(s)) { - resource.get(is -> assertEquals(1024, is.read(buf))); - } - if ("a/aa2".equals(s)) { - resource.get(is -> assertEquals(2 * 1024, is.read(buf))); - } - if ("a".equals(s)) { - resource.get(is -> assertEquals(-1, is.read())); - } - return Boolean.TRUE; - }); - assertEquals(5, allFiles.size()); - } -} diff --git a/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java b/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java index 8fd60619122..54f7406635d 100644 --- a/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java +++ b/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.util.Map; +import java.util.Set; import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.lucene99.Lucene99Codec.Mode; import org.apache.lucene.codecs.perfield.PerFieldDocValuesFormat; @@ -238,7 +239,7 @@ public void testCompressionModeDefault() throws IOException { config.get("codecFactory").attr("class")); assertTrue( "Unexpected configuration of codec factory for this test. Expecting empty element", - config.get("codecFactory").getAll(null, (String) null).isEmpty()); + config.get("codecFactory").getAll(null, Set.of()).isEmpty()); IndexSchema schema = IndexSchemaFactory.buildIndexSchema("schema_codec.xml", config); CoreContainer coreContainer = h.getCoreContainer(); diff --git a/solr/core/src/test/org/apache/solr/handler/RequestHandlerMetricsTest.java b/solr/core/src/test/org/apache/solr/handler/RequestHandlerMetricsTest.java index e3eecfc8cb3..b5b73de2597 100644 --- a/solr/core/src/test/org/apache/solr/handler/RequestHandlerMetricsTest.java +++ b/solr/core/src/test/org/apache/solr/handler/RequestHandlerMetricsTest.java @@ -90,11 +90,10 @@ public void testAggregateNodeLevelMetrics() throws SolrServerException, IOExcept final double[] minUpdateTime = {Double.MAX_VALUE}; final double[] maxUpdateTime = {-1.0}; Set> coreMetrics = new HashSet<>(); - metrics.forEachKey( - (key) -> { + metrics.forEach( + (key, coreMetric) -> { if (key.startsWith("solr.core.testRequestHandlerMetrics")) { - NamedList coreMetric = (NamedList) metrics.get(key); - coreMetrics.add(coreMetric); + coreMetrics.add((NamedList) coreMetric); } }); assertEquals(2, coreMetrics.size()); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerActionTest.java b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerActionTest.java index 235db7af21e..6b5daa5642d 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerActionTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerActionTest.java @@ -17,7 +17,6 @@ package org.apache.solr.handler.admin; import java.util.Locale; -import java.util.Map; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CoreAdminParams; @@ -82,11 +81,11 @@ private void testAction(String action, String propertyName, String propertyValue admin.handleRequestBody(req(CoreAdminParams.ACTION, action), response); - Map actionResponse = ((NamedList) response.getResponse()).asMap(); + var actionResponse = (NamedList) response.getResponse(); assertTrue( String.format(Locale.ROOT, "Action response should contain %s property", propertyName), - actionResponse.containsKey(propertyName)); + actionResponse.get(propertyName) != null); assertEquals( String.format( Locale.ROOT, diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/LazySolrCluster.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/LazySolrCluster.java deleted file mode 100644 index 811a0b4e2e8..00000000000 --- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/LazySolrCluster.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * 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.common; - -import static org.apache.solr.common.cloud.ZkStateReader.URL_SCHEME; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiConsumer; -import org.apache.solr.cluster.api.ApiType; -import org.apache.solr.cluster.api.CollectionConfig; -import org.apache.solr.cluster.api.HashRange; -import org.apache.solr.cluster.api.Resource; -import org.apache.solr.cluster.api.Router; -import org.apache.solr.cluster.api.Shard; -import org.apache.solr.cluster.api.ShardReplica; -import org.apache.solr.cluster.api.SimpleMap; -import org.apache.solr.cluster.api.SolrCluster; -import org.apache.solr.cluster.api.SolrCollection; -import org.apache.solr.cluster.api.SolrNode; -import org.apache.solr.common.cloud.Aliases; -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.common.util.Utils; -import org.apache.solr.common.util.WrappedSimpleMap; -import org.apache.zookeeper.KeeperException; - -/** - * Reference implementation for SolrCluster. As much as possible fetch all the values lazily because - * the value of anything can change any moment Creating an instance is a low cost operation. It does - * not result in a network call or large object creation - */ -public class LazySolrCluster implements SolrCluster { - final ZkStateReader zkStateReader; - - private final Map cached = new ConcurrentHashMap<>(); - private final SimpleMap collections; - private final SimpleMap collectionsAndAliases; - private final SimpleMap nodes; - private SimpleMap configs; - - public LazySolrCluster(ZkStateReader zkStateReader) { - this.zkStateReader = zkStateReader; - collections = lazyCollectionsMap(zkStateReader); - collectionsAndAliases = lazyCollectionsWithAlias(zkStateReader); - nodes = lazyNodeMap(); - } - - private SimpleMap lazyConfigMap() { - Set configNames = new HashSet<>(); - new SimpleZkMap(zkStateReader, ZkStateReader.CONFIGS_ZKNODE) - .abortableForEach( - (name, resource) -> { - if (!name.contains("/")) { - configNames.add(name); - return Boolean.TRUE; - } - return Boolean.FALSE; - }); - - return new SimpleMap() { - @Override - public CollectionConfig get(String key) { - if (configNames.contains(key)) { - return new ConfigImpl(key); - } else { - return null; - } - } - - @Override - public void forEachEntry(BiConsumer fun) { - for (String name : configNames) { - fun.accept(name, new ConfigImpl(name)); - } - } - - @Override - public int size() { - return configNames.size(); - } - }; - } - - private SimpleMap lazyNodeMap() { - return new SimpleMap() { - @Override - public SolrNode get(String key) { - if (!zkStateReader.getClusterState().liveNodesContain(key)) { - return null; - } - return new Node(key); - } - - @Override - public void forEachEntry(BiConsumer fun) { - for (String s : zkStateReader.getClusterState().getLiveNodes()) { - fun.accept(s, new Node(s)); - } - } - - @Override - public int size() { - return zkStateReader.getClusterState().getLiveNodes().size(); - } - }; - } - - private SimpleMap lazyCollectionsWithAlias(ZkStateReader zkStateReader) { - return new SimpleMap() { - @Override - public SolrCollection get(String key) { - SolrCollection result = collections.get(key); - if (result != null) return result; - Aliases aliases = zkStateReader.getAliases(); - List aliasNames = aliases.resolveAliases(key); - if (aliasNames == null || aliasNames.isEmpty()) return null; - return _collection(aliasNames.get(0), null); - } - - @Override - public void forEachEntry(BiConsumer fun) { - collections.forEachEntry(fun); - Aliases aliases = zkStateReader.getAliases(); - aliases.forEachAlias( - (s, colls) -> { - if (colls == null || colls.isEmpty()) return; - fun.accept(s, _collection(colls.get(0), null)); - }); - } - - @Override - public int size() { - return collections.size() + zkStateReader.getAliases().size(); - } - }; - } - - private SimpleMap lazyCollectionsMap(ZkStateReader zkStateReader) { - return new SimpleMap() { - @Override - public SolrCollection get(String key) { - return _collection(key, null); - } - - @Override - public void forEachEntry(BiConsumer fun) { - zkStateReader - .getClusterState() - .forEachCollection( - coll -> fun.accept(coll.getName(), _collection(coll.getName(), coll))); - } - - @Override - public int size() { - return zkStateReader.getClusterState().size(); - } - }; - } - - private SolrCollection _collection(String key, DocCollection c) { - if (c == null) c = zkStateReader.getCollection(key); - if (c == null) { - cached.remove(key); - return null; - } - SolrCollectionImpl existing = cached.get(key); - if (existing == null || !Objects.equals(existing.coll, c)) { - cached.put(key, existing = new SolrCollectionImpl(c, zkStateReader)); - } - return existing; - } - - @Override - public SimpleMap collections() throws SolrException { - return collections; - } - - @Override - public SimpleMap collections(boolean includeAlias) throws SolrException { - return includeAlias ? collectionsAndAliases : collections; - } - - @Override - public SimpleMap nodes() throws SolrException { - return nodes; - } - - @Override - public SimpleMap configs() throws SolrException { - if (configs == null) { - // these are lightweight objects and we don't care even if multiple objects ar ecreated b/c of - // a race condition - configs = lazyConfigMap(); - } - return configs; - } - - @Override - public String overseerNode() throws SolrException { - return null; - } - - @Override - public String thisNode() { - return null; - } - - private class SolrCollectionImpl implements SolrCollection { - final DocCollection coll; - final SimpleMap shards; - final ZkStateReader zkStateReader; - final Router router; - String confName; - - private SolrCollectionImpl(DocCollection coll, ZkStateReader zkStateReader) { - this.coll = coll; - this.zkStateReader = zkStateReader; - this.router = key -> coll.getRouter().getTargetSlice(key, null, null, null, null).getName(); - LinkedHashMap map = new LinkedHashMap<>(); - for (Slice slice : coll.getSlices()) { - map.put(slice.getName(), new ShardImpl(this, slice)); - } - shards = new WrappedSimpleMap<>(map); - } - - @Override - public String name() { - return coll.getName(); - } - - @Override - public SimpleMap shards() { - return shards; - } - - @Override - public String config() { - if (confName == null) { - // do this lazily . It's usually not necessary - try { - byte[] d = - zkStateReader - .getZkClient() - .getData(DocCollection.getCollectionPathRoot(coll.getName()), null, null, true); - if (d == null || d.length == 0) return null; - Map m = (Map) Utils.fromJSON(d); - confName = (String) m.get("configName"); - } catch (KeeperException | InterruptedException e) { - SimpleZkMap.throwZkExp(e); - // cannot read from ZK - return null; - } - } - return confName; - } - - @Override - public Router router() { - return router; - } - } - - private class ShardImpl implements Shard { - final SolrCollectionImpl collection; - final Slice slice; - final HashRange range; - final SimpleMap replicas; - - private ShardImpl(SolrCollectionImpl collection, Slice slice) { - this.collection = collection; - this.slice = slice; - range = _range(slice); - replicas = _replicas(); - } - - private SimpleMap _replicas() { - Map replicas = new HashMap<>(); - slice.forEach( - replica -> - replicas.put(replica.getName(), new ShardReplicaImpl(ShardImpl.this, replica))); - return new WrappedSimpleMap<>(replicas); - } - - private HashRange _range(Slice slice) { - return slice.getRange() == null - ? null - : new HashRange() { - @Override - public int min() { - return slice.getRange().min; - } - - @Override - public int max() { - return slice.getRange().max; - } - }; - } - - @Override - public String name() { - return slice.getName(); - } - - @Override - public String collection() { - return collection.name(); - } - - @Override - public HashRange range() { - return range; - } - - @Override - public SimpleMap replicas() { - return replicas; - } - - @Override - public String leader() { - Replica leader = slice.getLeader(); - return leader == null ? null : leader.getName(); - } - } - - private class ShardReplicaImpl implements ShardReplica { - private final ShardImpl shard; - private final Replica replica; - - private ShardReplicaImpl(ShardImpl shard, Replica replica) { - this.shard = shard; - this.replica = replica; - } - - @Override - public String name() { - return replica.getName(); - } - - @Override - public String shard() { - return shard.name(); - } - - @Override - public String collection() { - return shard.collection.name(); - } - - @Override - public String node() { - return replica.getNodeName(); - } - - @Override - public String core() { - return replica.getCoreName(); - } - - @Override - public Replica.Type type() { - return replica.getType(); - } - - @Override - public boolean alive() { - return zkStateReader.getClusterState().getLiveNodes().contains(node()) - && replica.getState() == Replica.State.ACTIVE; - } - - @Override - public long indexSize() { - // todo implement later - throw new UnsupportedOperationException("Not yet implemented"); - } - - @Override - public boolean isLeader() { - return Objects.equals(shard.leader(), name()); - } - - @Override - public String url(ApiType type) { - String base = nodes.get(node()).baseUrl(type); - if (type == ApiType.V2) { - return base + "/cores/" + core(); - } else { - return base + "/" + core(); - } - } - } - - private class Node implements SolrNode { - private final String name; - - private Node(String name) { - this.name = name; - } - - @Override - public String name() { - return name; - } - - @Override - public String baseUrl(ApiType apiType) { - return Utils.getBaseUrlForNodeName( - name, zkStateReader.getClusterProperty(URL_SCHEME, "http"), apiType == ApiType.V2); - } - - @Override - public SimpleMap cores() { - // todo implement later - // this requires a call to the node - throw new UnsupportedOperationException("Not yet implemented"); - } - } - - private class ConfigImpl implements CollectionConfig { - final String name; - final SimpleMap resources; - final String path; - - private ConfigImpl(String name) { - this.name = name; - path = ZkStateReader.CONFIGS_ZKNODE + "/" + name; - this.resources = new SimpleZkMap(zkStateReader, path); - } - - @Override - public SimpleMap resources() { - return resources; - } - - @Override - public String name() { - return name; - } - } -} diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java deleted file mode 100644 index 39051da857c..00000000000 --- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.common; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import org.apache.solr.cluster.api.Resource; -import org.apache.solr.cluster.api.SimpleMap; -import org.apache.solr.common.cloud.ZkStateReader; -import org.apache.zookeeper.KeeperException; - -/** - * A view of ZK as a {@link SimpleMap} impl. This gives a flat view of all paths instead of a tree - * view eg: a, b, c , a/a1, a/a2, a/a1/aa1 etc If possible, use the {@link - * #abortableForEach(BiFunction)} to traverse DO not use the {@link #size()} method. It always - * return 0 because it is very expensive to compute that - */ -public class SimpleZkMap implements SimpleMap { - private final ZkStateReader zkStateReader; - private final String basePath; - - static final byte[] EMPTY_BYTES = new byte[0]; - - public SimpleZkMap(ZkStateReader zkStateReader, String path) { - this.zkStateReader = zkStateReader; - this.basePath = path; - } - - @Override - public Resource get(String key) { - return readZkNode(basePath + key); - } - - @Override - public void abortableForEach(BiFunction fun) { - try { - recursiveRead("", zkStateReader.getZkClient().getChildren(basePath, null, true), fun); - } catch (KeeperException | InterruptedException e) { - throwZkExp(e); - } - } - - @Override - public void forEachEntry(BiConsumer fun) { - abortableForEach( - (path, resource) -> { - fun.accept(path, resource); - return Boolean.TRUE; - }); - } - - @Override - public int size() { - return 0; - } - - private Resource readZkNode(String path) { - return new Resource() { - @Override - public String name() { - return path; - } - - @Override - public void get(Consumer consumer) throws SolrException { - try { - byte[] data = - zkStateReader.getZkClient().getData(basePath + "/" + path, null, null, true); - if (data != null && data.length > 0) { - consumer.read(new ByteArrayInputStream(data)); - } else { - consumer.read(new ByteArrayInputStream(EMPTY_BYTES)); - } - } catch (KeeperException | InterruptedException e) { - throwZkExp(e); - } catch (IOException e) { - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Can;t read stream", e); - } - } - }; - } - - private boolean recursiveRead( - String parent, List childrenList, BiFunction fun) { - if (childrenList == null || childrenList.isEmpty()) return true; - try { - Map> withKids = new LinkedHashMap<>(); - for (String child : childrenList) { - String relativePath = parent.isEmpty() ? child : parent + "/" + child; - if (!fun.apply(relativePath, readZkNode(relativePath))) return false; - List l1 = - zkStateReader.getZkClient().getChildren(basePath + "/" + relativePath, null, true); - if (l1 != null && !l1.isEmpty()) { - withKids.put(relativePath, l1); - } - } - // now we iterate through all nodes with sub paths - for (Map.Entry> e : withKids.entrySet()) { - // has children - if (!recursiveRead(e.getKey(), e.getValue(), fun)) { - return false; - } - } - } catch (KeeperException | InterruptedException e) { - throwZkExp(e); - } - return true; - } - - static void throwZkExp(Exception e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "ZK errror", e); - } -} diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/PerReplicaStatesOps.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/PerReplicaStatesOps.java index 85fcef00e83..781289df2f7 100644 --- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/PerReplicaStatesOps.java +++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/PerReplicaStatesOps.java @@ -212,7 +212,7 @@ public static PerReplicaStatesOps disable(PerReplicaStates rs) { new PerReplicaStatesOps( prs -> { List result = new ArrayList<>(); - prs.states.forEachEntry( + prs.states.forEach( (s, state) -> result.add( new PerReplicaStates.Operation( diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java b/solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java deleted file mode 100644 index 1d4502cd3ce..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.cluster.api; - -/** Types of API calls */ -public enum ApiType { - V1("solr"), - V2("api"); - final String prefix; - - ApiType(String prefix) { - this.prefix = prefix; - } -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/CollectionConfig.java b/solr/solrj/src/java/org/apache/solr/cluster/api/CollectionConfig.java deleted file mode 100644 index 020c57fc055..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/CollectionConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.cluster.api; - -public interface CollectionConfig { - - String name(); - - SimpleMap resources(); -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java b/solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java deleted file mode 100644 index 23d8fab1325..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.cluster.api; - -/** A range of hash that is stored in a shard */ -public interface HashRange { - - /** minimum value (inclusive) */ - int min(); - - /** maximum value (inclusive) */ - int max(); - - /** Check if a given hash falls in this range */ - default boolean includes(int hash) { - return hash >= min() && hash <= max(); - } - - /** Check if another range is a subset of this range */ - default boolean isSubset(HashRange subset) { - return min() <= subset.min() && max() >= subset.max(); - } -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java b/solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java deleted file mode 100644 index 13548f6a822..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.cluster.api; - -import java.io.IOException; -import java.io.InputStream; -import org.apache.solr.common.SolrException; - -/** A binary resource. The impl is agnostic of the content type */ -public interface Resource { - /** This is a full path. e.g schema.xml, solrconfig.xml, lang/stopwords.txt etc */ - String name(); - - /** - * read a file/resource. The caller should consume the stream completely and should not hold a - * reference to this stream. This method closes the stream soon after the method returns - * - * @param resourceConsumer This should be a full path. e.g schema.xml, solrconfig.xml, - * lang/stopwords.txt etc - */ - void get(Consumer resourceConsumer) throws SolrException; - - interface Consumer { - void read(InputStream is) throws IOException; - } -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/Router.java b/solr/solrj/src/java/org/apache/solr/cluster/api/Router.java deleted file mode 100644 index ae6f6336ba4..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/Router.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.cluster.api; - -/** identify shards for a given routing key or document id */ -public interface Router { - - /** shard name for a given routing key */ - String shard(String routingKey); -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java b/solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java deleted file mode 100644 index 0b3cfc15abd..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.cluster.api; - -/** A shard of a collection */ -public interface Shard { - - /** name of the shard */ - String name(); - - /** collection this shard belongs to */ - String collection(); - - /** hash range of this shard. null if this is not using hash based router */ - HashRange range(); - - /** replicas of the shard */ - SimpleMap replicas(); - - /** Name of the replica that is acting as the leader at the moment */ - String leader(); -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java b/solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java deleted file mode 100644 index 57b133a0ad6..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.cluster.api; - -import org.apache.solr.common.cloud.Replica; - -/** replica of a shard */ -public interface ShardReplica { - /** Name of this replica */ - String name(); - - /** The shard which it belongs to */ - String shard(); - - /** collection which it belongs to */ - String collection(); - - /** Name of the node where this replica is present */ - String node(); - - /** Name of the core where this is hosted */ - String core(); - - /** type of the replica */ - Replica.Type type(); - - /** Is the replica alive now */ - boolean alive(); - - /** - * Size of the index in bytes. Keep in mind that this may result in a network call. Also keep in - * mind that the value that you get is at best an approximation. The exact size may vary from - * replica to replica - */ - long indexSize(); - - /** Is this replica the leader */ - boolean isLeader(); - - /** Baseurl for this replica */ - String url(ApiType type); -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java deleted file mode 100644 index 4398d12742f..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.cluster.api; - -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import org.apache.solr.common.MapWriter; - -/** - * A simplified read-only key-value structure. It is designed to support large datasets without - * consuming a lot of memory The objective is to provide implementations that are cheap and memory - * efficient to implement and consume. The keys are always {@link CharSequence} objects, The values - * can be of any type - */ -public interface SimpleMap extends MapWriter { - - /** get a value by key. If not present , null is returned */ - T get(String key); - - default T get(String key, T def) { - T val = get(key); - return val == null ? def : val; - } - - /** Navigate through all keys and values */ - void forEachEntry(BiConsumer fun); - - /** - * iterate through all keys The default impl is suboptimal. Proper implementations must do it more - * efficiently - */ - default void forEachKey(Consumer fun) { - forEachEntry((k, t) -> fun.accept(k)); - } - - int size(); - - /** - * iterate through all keys but abort in between if required The default impl is suboptimal. - * Proper implementations must do it more efficiently - * - * @param fun Consume each key and return a boolean to signal whether to proceed or not. If true, - * continue. If false stop - */ - default void abortableForEachKey(Function fun) { - abortableForEach((key, t) -> fun.apply(key)); - } - - /** - * Navigate through all key-values but abort in between if required. The default impl is - * suboptimal. Proper implementations must do it more efficiently - * - * @param fun Consume each entry and return a boolean to signal whether to proceed or not. If - * true, continue, if false stop - */ - default void abortableForEach(BiFunction fun) { - forEachEntry( - new BiConsumer<>() { - boolean end = false; - - @Override - public void accept(String k, T v) { - if (end) return; - end = fun.apply(k, v); - } - }); - } - - @Override - default void writeMap(EntryWriter ew) throws IOException { - forEachEntry(ew::putNoEx); - } - - default Map asMap(Map sink) { - forEachEntry(sink::put); - return sink; - } - - default Map asMap() { - return asMap(new LinkedHashMap<>()); - } -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java deleted file mode 100644 index 301cd57655b..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.cluster.api; - -import org.apache.solr.common.SolrException; - -/** Represents a Solr cluster */ -public interface SolrCluster { - - /** collections in the cluster */ - SimpleMap collections() throws SolrException; - - /** collections in the cluster and aliases */ - SimpleMap collections(boolean includeAlias) throws SolrException; - - /** nodes in the cluster */ - SimpleMap nodes() throws SolrException; - - /** Config sets in the cluster */ - SimpleMap configs() throws SolrException; - - /** Name of the node in which the overseer is running */ - String overseerNode() throws SolrException; - - /** - * The name of the node in which this method is invoked from. returns null, if this is not invoked - * from a Solr node - */ - String thisNode(); -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCollection.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCollection.java deleted file mode 100644 index df9007e3b7f..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCollection.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.cluster.api; - -/** Represents a collection in Solr */ -public interface SolrCollection { - - String name(); - - /** shards of a collection */ - SimpleMap shards(); - - /** Name of the configset used by this collection */ - String config(); - - /** Router used in this collection */ - Router router(); -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java deleted file mode 100644 index 13115643c12..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.cluster.api; - -/** A read only view of a Solr node */ -public interface SolrNode { - - /** The node name */ - String name(); - - /** Base http url for this node */ - String baseUrl(ApiType type); - - /** - * Get all the cores in a given node. This usually involves a network call. So, it's likely to be - * expensive - */ - SimpleMap cores(); -} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java b/solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java deleted file mode 100644 index 9c4183d49da..00000000000 --- a/solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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. - */ - -/** API interfaces for core SolrCloud classes */ -package org.apache.solr.cluster.api; diff --git a/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java b/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java index be86c1e5c7b..64e5ec8ce0f 100644 --- a/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java +++ b/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java @@ -26,12 +26,11 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; -import org.apache.solr.cluster.api.SimpleMap; -import org.apache.solr.common.util.WrappedSimpleMap; /** * A generic interface that represents a config file, mostly XML Please note that this is an @@ -44,7 +43,7 @@ public interface ConfigNode { String name(); /** Attributes */ - SimpleMap attributes(); + Map attributes(); /** Child by name */ default ConfigNode child(String name) { @@ -195,7 +194,7 @@ public String txt() { } @Override - public SimpleMap attributes() { + public Map attributes() { return empty_attrs; } @@ -233,7 +232,7 @@ public boolean isNull() { public void forEachChild(Function fun) {} }; - SimpleMap empty_attrs = new WrappedSimpleMap<>(Collections.emptyMap()); + Map empty_attrs = Map.of(); class Helpers { static boolean _bool(Object v, boolean def) { diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/DocRouter.java b/solr/solrj/src/java/org/apache/solr/common/cloud/DocRouter.java index 3c8ff93b816..82265675224 100644 --- a/solr/solrj/src/java/org/apache/solr/common/cloud/DocRouter.java +++ b/solr/solrj/src/java/org/apache/solr/common/cloud/DocRouter.java @@ -24,7 +24,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import org.apache.solr.cluster.api.HashRange; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.cloud.DocCollection.CollectionStateProps; @@ -89,7 +88,7 @@ public static Map getRouterSpec(ZkNodeProps props) { // Hash ranges can't currently "wrap" - i.e. max must be greater or equal to min. // TODO: ranges may not be all contiguous in the future (either that or we will // need an extra class to model a collection of ranges) - public static class Range implements JSONWriter.Writable, Comparable, HashRange { + public static class Range implements JSONWriter.Writable, Comparable { public int min; // inclusive public int max; // inclusive @@ -99,17 +98,14 @@ public Range(int min, int max) { this.max = max; } - @Override public int min() { return min; } - @Override public int max() { return max; } - @Override public boolean includes(int hash) { return hash >= min && hash <= max; } diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/PerReplicaStates.java b/solr/solrj/src/java/org/apache/solr/common/cloud/PerReplicaStates.java index 60616d8fd70..961f3efc50d 100644 --- a/solr/solrj/src/java/org/apache/solr/common/cloud/PerReplicaStates.java +++ b/solr/solrj/src/java/org/apache/solr/common/cloud/PerReplicaStates.java @@ -31,14 +31,12 @@ import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; -import org.apache.solr.cluster.api.SimpleMap; import org.apache.solr.common.IteratorWriter; import org.apache.solr.common.MapWriter; import org.apache.solr.common.annotation.JsonProperty; import org.apache.solr.common.cloud.Replica.ReplicaStateProps; import org.apache.solr.common.util.ReflectMapWriter; import org.apache.solr.common.util.StrUtils; -import org.apache.solr.common.util.WrappedSimpleMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,7 +57,7 @@ public class PerReplicaStates implements ReflectMapWriter { @JsonProperty public final int cversion; // states of individual replicas - @JsonProperty public final SimpleMap states; + @JsonProperty public final Map states; private volatile Boolean allActive; @@ -85,7 +83,7 @@ public PerReplicaStates(String path, int cversion, List states) { tmp.put(rs.replica, rs.insert(existing)); } } - this.states = new WrappedSimpleMap<>(tmp); + this.states = tmp; } public static PerReplicaStates empty(String collectionName) { @@ -95,27 +93,23 @@ public static PerReplicaStates empty(String collectionName) { /** Check and return if all replicas are ACTIVE */ public boolean allActive() { if (this.allActive != null) return allActive; - boolean[] result = new boolean[] {true}; - states.forEachEntry( - (r, s) -> { - if (s.state != Replica.State.ACTIVE) result[0] = false; - }); - return this.allActive = result[0]; + this.allActive = states.values().stream().allMatch(s -> s.state == Replica.State.ACTIVE); + return allActive; } /** Get the changed replicas */ public static Set findModifiedReplicas(PerReplicaStates old, PerReplicaStates fresh) { Set result = new HashSet<>(); if (fresh == null) { - old.states.forEachKey(result::add); + result.addAll(old.states.keySet()); return result; } - old.states.forEachEntry( + old.states.forEach( (s, state) -> { // the state is modified or missing if (!Objects.equals(fresh.get(s), state)) result.add(s); }); - fresh.states.forEachEntry( + fresh.states.forEach( (s, state) -> { if (old.get(s) == null) result.add(s); }); @@ -165,8 +159,8 @@ public String toString() { } private StringBuilder appendStates(StringBuilder sb) { - states.forEachEntry( - new BiConsumer() { + states.forEach( + new BiConsumer<>() { int count = 0; @Override @@ -318,7 +312,7 @@ public EntryWriter put(CharSequence k, Object v) throws IOException { ew.put( "states", (IteratorWriter) - iw -> states.forEachEntry((s, state) -> iw.addNoEx(state.toString()))); + iw -> states.forEach((s, state) -> iw.addNoEx(state.toString()))); } else { ew.put(k, v); } diff --git a/solr/solrj/src/java/org/apache/solr/common/util/DOMUtil.java b/solr/solrj/src/java/org/apache/solr/common/util/DOMUtil.java index 840c64b5d87..0ef6bfbe538 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/DOMUtil.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/DOMUtil.java @@ -50,7 +50,7 @@ public static Map toMap(ConfigNode node) { public static Map toMapExcept(ConfigNode node, String... exclusions) { Map args = new HashMap<>(); node.attributes() - .forEachEntry( + .forEach( (k, v) -> { for (String ex : exclusions) if (ex.equals(k)) return; args.put(k, v); diff --git a/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java b/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java index aef0edaa4cf..deb05dd22b2 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java @@ -30,10 +30,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import org.apache.solr.cluster.api.SimpleMap; import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.MultiMapSolrParams; @@ -61,7 +57,7 @@ */ @SuppressWarnings({"unchecked", "rawtypes"}) public class NamedList - implements Cloneable, Serializable, Iterable>, MapWriter, SimpleMap { + implements Cloneable, Serializable, Iterable>, MapWriter { private static final long serialVersionUID = 1957981902839867821L; protected final List nvPairs; @@ -156,7 +152,6 @@ private List nameValueMapToList(Map.Entry[] nameVal } /** The total number of name/value pairs */ - @Override public int size() { return nvPairs.size() >> 1; } @@ -248,11 +243,16 @@ public int indexOf(String name, int start) { * @see #indexOf * @see #get(String,int) */ - @Override public T get(String name) { return get(name, 0); } + /** Like {@link #get(String)} but returns a default value if it would be null. */ + public T getOrDefault(String name, T def) { + T val = get(name); + return val == null ? def : val; + } + /** * Gets the value for the first instance of the specified name found starting at the specified * index. @@ -486,7 +486,7 @@ public Set> entrySet() { @Override public void forEach(BiConsumer action) { - NamedList.this.forEachEntry(action); + NamedList.this.forEach(action); } }; } @@ -818,30 +818,6 @@ public boolean equals(Object obj) { return this.nvPairs.equals(nl.nvPairs); } - @Override - public void abortableForEach(BiFunction fun) { - int sz = size(); - for (int i = 0; i < sz; i++) { - if (!fun.apply(getName(i), getVal(i))) break; - } - } - - @Override - public void abortableForEachKey(Function fun) { - int sz = size(); - for (int i = 0; i < sz; i++) { - if (!fun.apply(getName(i))) break; - } - } - - @Override - public void forEachKey(Consumer fun) { - int sz = size(); - for (int i = 0; i < sz; i++) { - fun.accept(getName(i)); - } - } - public void forEach(BiConsumer action) { int sz = size(); for (int i = 0; i < sz; i++) { @@ -853,9 +829,4 @@ public void forEach(BiConsumer action) { public int _size() { return size(); } - - @Override - public void forEachEntry(BiConsumer fun) { - forEach(fun); - } } diff --git a/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java b/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java deleted file mode 100644 index 417e8d3c968..00000000000 --- a/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.common.util; - -import java.util.Collections; -import java.util.Map; -import java.util.function.BiConsumer; -import org.apache.solr.cluster.api.SimpleMap; - -public class WrappedSimpleMap implements SimpleMap { - private final Map delegate; - - @Override - public T get(String key) { - return delegate.get(key); - } - - @Override - public void forEachEntry(BiConsumer fun) { - delegate.forEach(fun); - } - - @Override - public int size() { - return delegate.size(); - } - - public WrappedSimpleMap(Map delegate) { - this.delegate = delegate; - } - - @Override - public Map asMap(Map sink) { - sink.putAll(delegate); - return sink; - } - - @Override - public Map asMap() { - return Collections.unmodifiableMap(delegate); - } -} diff --git a/solr/solrj/src/test/org/apache/solr/common/cloud/PerReplicaStatesIntegrationTest.java b/solr/solrj/src/test/org/apache/solr/common/cloud/PerReplicaStatesIntegrationTest.java index 21a5307eb67..71336a8088d 100644 --- a/solr/solrj/src/test/org/apache/solr/common/cloud/PerReplicaStatesIntegrationTest.java +++ b/solr/solrj/src/test/org/apache/solr/common/cloud/PerReplicaStatesIntegrationTest.java @@ -164,7 +164,7 @@ public void testRestart() throws Exception { PerReplicaStatesOps.fetch(collectionPath, SolrCloudTestCase.cluster.getZkClient(), null); assertEquals(2, prs.states.size()); c = cluster.getZkStateReader().getCollection(testCollection); - prs.states.forEachEntry((s, state) -> assertEquals(Replica.State.ACTIVE, state.state)); + prs.states.forEach((s, state) -> assertEquals(Replica.State.ACTIVE, state.state)); String replicaName = null; for (Replica r : c.getSlice("shard1").getReplicas()) { @@ -226,7 +226,7 @@ public void testRestart() throws Exception { prs = PerReplicaStatesOps.fetch( collectionPath, SolrCloudTestCase.cluster.getZkClient(), null); - prs.states.forEachEntry((s, state) -> assertEquals(Replica.State.ACTIVE, state.state)); + prs.states.forEach((s, state) -> assertEquals(Replica.State.ACTIVE, state.state)); } } finally { @@ -286,7 +286,7 @@ public void testMultipleTransitions() throws Exception { PerReplicaStates prs2 = PerReplicaStatesOps.fetch( DocCollection.getCollectionPath(COLL), cluster.getZkClient(), null); - prs2.states.forEachEntry( + prs2.states.forEach( (r, newState) -> { if (newState.getDuplicate() != null) anyFail.set(true); }); From 5e8a5c5108b9f6e82ad2359ac90c9aed2a97a07a Mon Sep 17 00:00:00 2001 From: David Smiley Date: Fri, 8 Nov 2024 21:10:59 -0500 Subject: [PATCH 2/7] forEach --- solr/core/src/java/org/apache/solr/util/DataConfigNode.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/solr/core/src/java/org/apache/solr/util/DataConfigNode.java b/solr/core/src/java/org/apache/solr/util/DataConfigNode.java index fde5693534a..4711e644cb4 100644 --- a/solr/core/src/java/org/apache/solr/util/DataConfigNode.java +++ b/solr/core/src/java/org/apache/solr/util/DataConfigNode.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; import org.apache.solr.common.ConfigNode; @@ -142,6 +143,11 @@ public Set keySet() { return delegate.keySet(); } + @Override + public void forEach(BiConsumer action) { + delegate.forEach((k, v) -> action.accept(k, substituteVal(v))); + } + @Override public Set> entrySet() { return new AbstractSet<>() { From 6b3f5d09165f7416dccffd365dd0579c8a893609 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Tue, 12 Nov 2024 23:14:52 -0500 Subject: [PATCH 3/7] Mostly reinstate some asMap calls that should have been that. --- solr/core/src/java/org/apache/solr/cli/ApiTool.java | 2 +- solr/core/src/java/org/apache/solr/cli/CreateTool.java | 2 +- .../apache/solr/search/facet/SpatialHeatmapFacetsTest.java | 3 +-- solr/solrj/src/java/org/apache/solr/common/ConfigNode.java | 4 +--- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/cli/ApiTool.java b/solr/core/src/java/org/apache/solr/cli/ApiTool.java index c1887cfbc14..2bce06a70ab 100644 --- a/solr/core/src/java/org/apache/solr/cli/ApiTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ApiTool.java @@ -98,7 +98,7 @@ protected String callGet(String url, String credentials) throws Exception { NamedList response = solrClient.request(req); // pretty-print the response to stdout CharArr arr = new CharArr(); - new JSONWriter(arr, 2).write(response.asShallowMap()); + new JSONWriter(arr, 2).write(response.asMap(10)); return arr.toString(); } } diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java index 071b37dc6af..babfaf1d74d 100644 --- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java @@ -319,7 +319,7 @@ protected void createCollection(CloudSolrClient cloudSolrClient, CommandLine cli if (isVerbose()) { // pretty-print the response to stdout CharArr arr = new CharArr(); - new JSONWriter(arr, 2).write(response.asShallowMap()); + new JSONWriter(arr, 2).write(response.asMap(10)); echo(arr.toString()); } String endMessage = diff --git a/solr/core/src/test/org/apache/solr/search/facet/SpatialHeatmapFacetsTest.java b/solr/core/src/test/org/apache/solr/search/facet/SpatialHeatmapFacetsTest.java index 185e9693860..da2a76a3141 100644 --- a/solr/core/src/test/org/apache/solr/search/facet/SpatialHeatmapFacetsTest.java +++ b/solr/core/src/test/org/apache/solr/search/facet/SpatialHeatmapFacetsTest.java @@ -156,8 +156,7 @@ public void testClassicFacets() throws Exception { // AKA SimpleFacets assertTrue( ((NamedList) response.getResponse().findRecursive("facet_counts", "facet_heatmaps", "course")) - .asMap(0) - .containsKey("counts_" + courseFormat)); + .get("counts_" + courseFormat) != null); } // ------ Index data diff --git a/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java b/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java index 64e5ec8ce0f..18022605f69 100644 --- a/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java +++ b/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java @@ -195,7 +195,7 @@ public String txt() { @Override public Map attributes() { - return empty_attrs; + return Map.of(); } @Override @@ -232,8 +232,6 @@ public boolean isNull() { public void forEachChild(Function fun) {} }; - Map empty_attrs = Map.of(); - class Helpers { static boolean _bool(Object v, boolean def) { return v == null ? def : Boolean.parseBoolean(v.toString()); From 6370577bb6f9c3e71f475e15f880fa1719eb8bd8 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Tue, 12 Nov 2024 23:59:25 -0500 Subject: [PATCH 4/7] Fix SpatialHeatmapFacetsTest --- .../apache/solr/search/facet/SpatialHeatmapFacetsTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/search/facet/SpatialHeatmapFacetsTest.java b/solr/core/src/test/org/apache/solr/search/facet/SpatialHeatmapFacetsTest.java index da2a76a3141..a385eeeb498 100644 --- a/solr/core/src/test/org/apache/solr/search/facet/SpatialHeatmapFacetsTest.java +++ b/solr/core/src/test/org/apache/solr/search/facet/SpatialHeatmapFacetsTest.java @@ -155,8 +155,11 @@ public void testClassicFacets() throws Exception { // AKA SimpleFacets .findRecursive("facet_counts", "facet_heatmaps", "course", "gridLevel")); assertTrue( ((NamedList) - response.getResponse().findRecursive("facet_counts", "facet_heatmaps", "course")) - .get("counts_" + courseFormat) != null); + response + .getResponse() + .findRecursive("facet_counts", "facet_heatmaps", "course")) + .indexOf("counts_" + courseFormat, 0) + >= 0); } // ------ Index data From 71c0ed10c6b3e46c8d6a9b4e71cd4cd6cf73607d Mon Sep 17 00:00:00 2001 From: David Smiley Date: Sun, 8 Dec 2024 22:51:24 -0500 Subject: [PATCH 5/7] move CLIUtils change after the refactor --- solr/core/src/java/org/apache/solr/cli/CLIUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java index 7f4c0dc648b..b3336eb2361 100644 --- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java +++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java @@ -328,7 +328,7 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin Map failureStatus = (Map) existsCheckResult.get("initFailures"); String errorMsg = (String) failureStatus.get(coreName); - final boolean hasName = coreStatus != null && coreStatus.asMap().containsKey(NAME); + final boolean hasName = coreStatus != null && coreStatus.get(NAME) != null; exists = hasName || errorMsg != null; wait = hasName && errorMsg == null && "true".equals(coreStatus.get("isLoading")); } while (wait && System.nanoTime() - startWaitAt < MAX_WAIT_FOR_CORE_LOAD_NANOS); From f1017f61275d3200b58c682282f5e39b95a81971 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Tue, 10 Dec 2024 01:07:08 -0500 Subject: [PATCH 6/7] Un-delete but deprecate the files --- .../solr/cloud/TestLazySolrCluster.java | 104 ++++ .../apache/solr/common/LazySolrCluster.java | 466 ++++++++++++++++++ .../org/apache/solr/common/SimpleZkMap.java | 137 +++++ .../org/apache/solr/cluster/api/ApiType.java | 30 ++ .../solr/cluster/api/CollectionConfig.java | 26 + .../apache/solr/cluster/api/HashRange.java | 39 ++ .../org/apache/solr/cluster/api/Resource.java | 42 ++ .../org/apache/solr/cluster/api/Router.java | 26 + .../org/apache/solr/cluster/api/Shard.java | 38 ++ .../apache/solr/cluster/api/ShardReplica.java | 58 +++ .../apache/solr/cluster/api/SimpleMap.java | 110 +++++ .../apache/solr/cluster/api/SolrCluster.java | 46 ++ .../solr/cluster/api/SolrCollection.java | 34 ++ .../org/apache/solr/cluster/api/SolrNode.java | 35 ++ .../apache/solr/cluster/api/package-info.java | 20 + .../solr/common/util/WrappedSimpleMap.java | 58 +++ 16 files changed, 1269 insertions(+) create mode 100644 solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java create mode 100644 solr/solrj-zookeeper/src/java/org/apache/solr/common/LazySolrCluster.java create mode 100644 solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/CollectionConfig.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/Router.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/SolrCollection.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java create mode 100644 solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java create mode 100644 solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java diff --git a/solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java b/solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java new file mode 100644 index 00000000000..7d7f7024953 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java @@ -0,0 +1,104 @@ +/* + * 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.cloud; + +import java.util.ArrayList; +import java.util.List; +import org.apache.solr.client.solrj.impl.CloudSolrClient; +import org.apache.solr.client.solrj.request.CollectionAdminRequest; +import org.apache.solr.cluster.api.CollectionConfig; +import org.apache.solr.cluster.api.SimpleMap; +import org.apache.solr.cluster.api.SolrCollection; +import org.apache.solr.common.LazySolrCluster; +import org.apache.solr.common.cloud.SolrZkClient; +import org.apache.solr.common.cloud.ZkStateReader; +import org.apache.zookeeper.CreateMode; +import org.junit.BeforeClass; + +public class TestLazySolrCluster extends SolrCloudTestCase { + @BeforeClass + public static void setupCluster() throws Exception { + configureCluster(5) + .addConfig( + "conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf")) + .configure(); + } + + public void test() throws Exception { + CloudSolrClient cloudClient = cluster.getSolrClient(); + String collection = "testLazyCluster1"; + cloudClient.request(CollectionAdminRequest.createCollection(collection, "conf1", 2, 2)); + cluster.waitForActiveCollection(collection, 2, 4); + collection = "testLazyCluster2"; + cloudClient.request(CollectionAdminRequest.createCollection(collection, "conf1", 2, 2)); + cluster.waitForActiveCollection(collection, 2, 4); + + LazySolrCluster solrCluster = new LazySolrCluster(ZkStateReader.from(cloudClient)); + SimpleMap colls = solrCluster.collections(); + + SolrCollection c = colls.get("testLazyCluster1"); + assertNotNull(c); + c = colls.get("testLazyCluster2"); + assertNotNull(c); + int[] count = new int[1]; + solrCluster.collections().forEachEntry((s, solrCollection) -> count[0]++); + assertEquals(2, count[0]); + + count[0] = 0; + + assertEquals(2, solrCluster.collections().get("testLazyCluster1").shards().size()); + solrCluster + .collections() + .get("testLazyCluster1") + .shards() + .forEachEntry((s, shard) -> shard.replicas().forEachEntry((s1, replica) -> count[0]++)); + assertEquals(4, count[0]); + + assertEquals(5, solrCluster.nodes().size()); + SolrZkClient zkClient = ZkStateReader.from(cloudClient).getZkClient(); + zkClient.create(ZkStateReader.CONFIGS_ZKNODE + "/conf1/a", null, CreateMode.PERSISTENT, true); + zkClient.create( + ZkStateReader.CONFIGS_ZKNODE + "/conf1/a/aa1", new byte[1024], CreateMode.PERSISTENT, true); + zkClient.create( + ZkStateReader.CONFIGS_ZKNODE + "/conf1/a/aa2", + new byte[1024 * 2], + CreateMode.PERSISTENT, + true); + + List allFiles = new ArrayList<>(); + byte[] buf = new byte[3 * 1024]; + CollectionConfig conf1 = solrCluster.configs().get("conf1"); + conf1 + .resources() + .abortableForEach( + (s, resource) -> { + allFiles.add(s); + if ("a/aa1".equals(s)) { + resource.get(is -> assertEquals(1024, is.read(buf))); + } + if ("a/aa2".equals(s)) { + resource.get(is -> assertEquals(2 * 1024, is.read(buf))); + } + if ("a".equals(s)) { + resource.get(is -> assertEquals(-1, is.read())); + } + return Boolean.TRUE; + }); + assertEquals(5, allFiles.size()); + } +} diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/LazySolrCluster.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/LazySolrCluster.java new file mode 100644 index 00000000000..6dc597ad646 --- /dev/null +++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/LazySolrCluster.java @@ -0,0 +1,466 @@ +/* + * 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.common; + +import static org.apache.solr.common.cloud.ZkStateReader.URL_SCHEME; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; +import org.apache.solr.cluster.api.ApiType; +import org.apache.solr.cluster.api.CollectionConfig; +import org.apache.solr.cluster.api.HashRange; +import org.apache.solr.cluster.api.Resource; +import org.apache.solr.cluster.api.Router; +import org.apache.solr.cluster.api.Shard; +import org.apache.solr.cluster.api.ShardReplica; +import org.apache.solr.cluster.api.SimpleMap; +import org.apache.solr.cluster.api.SolrCluster; +import org.apache.solr.cluster.api.SolrCollection; +import org.apache.solr.cluster.api.SolrNode; +import org.apache.solr.common.cloud.Aliases; +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.common.util.Utils; +import org.apache.solr.common.util.WrappedSimpleMap; +import org.apache.zookeeper.KeeperException; + +/** + * Reference implementation for SolrCluster. As much as possible fetch all the values lazily because + * the value of anything can change any moment Creating an instance is a low cost operation. It does + * not result in a network call or large object creation + */ +@Deprecated +public class LazySolrCluster implements SolrCluster { + final ZkStateReader zkStateReader; + + private final Map cached = new ConcurrentHashMap<>(); + private final SimpleMap collections; + private final SimpleMap collectionsAndAliases; + private final SimpleMap nodes; + private SimpleMap configs; + + public LazySolrCluster(ZkStateReader zkStateReader) { + this.zkStateReader = zkStateReader; + collections = lazyCollectionsMap(zkStateReader); + collectionsAndAliases = lazyCollectionsWithAlias(zkStateReader); + nodes = lazyNodeMap(); + } + + private SimpleMap lazyConfigMap() { + Set configNames = new HashSet<>(); + new SimpleZkMap(zkStateReader, ZkStateReader.CONFIGS_ZKNODE) + .abortableForEach( + (name, resource) -> { + if (!name.contains("/")) { + configNames.add(name); + return Boolean.TRUE; + } + return Boolean.FALSE; + }); + + return new SimpleMap() { + @Override + public CollectionConfig get(String key) { + if (configNames.contains(key)) { + return new ConfigImpl(key); + } else { + return null; + } + } + + @Override + public void forEachEntry(BiConsumer fun) { + for (String name : configNames) { + fun.accept(name, new ConfigImpl(name)); + } + } + + @Override + public int size() { + return configNames.size(); + } + }; + } + + private SimpleMap lazyNodeMap() { + return new SimpleMap() { + @Override + public SolrNode get(String key) { + if (!zkStateReader.getClusterState().liveNodesContain(key)) { + return null; + } + return new Node(key); + } + + @Override + public void forEachEntry(BiConsumer fun) { + for (String s : zkStateReader.getClusterState().getLiveNodes()) { + fun.accept(s, new Node(s)); + } + } + + @Override + public int size() { + return zkStateReader.getClusterState().getLiveNodes().size(); + } + }; + } + + private SimpleMap lazyCollectionsWithAlias(ZkStateReader zkStateReader) { + return new SimpleMap() { + @Override + public SolrCollection get(String key) { + SolrCollection result = collections.get(key); + if (result != null) return result; + Aliases aliases = zkStateReader.getAliases(); + List aliasNames = aliases.resolveAliases(key); + if (aliasNames == null || aliasNames.isEmpty()) return null; + return _collection(aliasNames.get(0), null); + } + + @Override + public void forEachEntry(BiConsumer fun) { + collections.forEachEntry(fun); + Aliases aliases = zkStateReader.getAliases(); + aliases.forEachAlias( + (s, colls) -> { + if (colls == null || colls.isEmpty()) return; + fun.accept(s, _collection(colls.get(0), null)); + }); + } + + @Override + public int size() { + return collections.size() + zkStateReader.getAliases().size(); + } + }; + } + + private SimpleMap lazyCollectionsMap(ZkStateReader zkStateReader) { + return new SimpleMap() { + @Override + public SolrCollection get(String key) { + return _collection(key, null); + } + + @Override + public void forEachEntry(BiConsumer fun) { + zkStateReader + .getClusterState() + .collectionStream() + .forEach(coll -> fun.accept(coll.getName(), _collection(coll.getName(), coll))); + } + + @Override + public int size() { + return zkStateReader.getClusterState().size(); + } + }; + } + + private SolrCollection _collection(String key, DocCollection c) { + if (c == null) c = zkStateReader.getCollection(key); + if (c == null) { + cached.remove(key); + return null; + } + SolrCollectionImpl existing = cached.get(key); + if (existing == null || !Objects.equals(existing.coll, c)) { + cached.put(key, existing = new SolrCollectionImpl(c, zkStateReader)); + } + return existing; + } + + @Override + public SimpleMap collections() throws SolrException { + return collections; + } + + @Override + public SimpleMap collections(boolean includeAlias) throws SolrException { + return includeAlias ? collectionsAndAliases : collections; + } + + @Override + public SimpleMap nodes() throws SolrException { + return nodes; + } + + @Override + public SimpleMap configs() throws SolrException { + if (configs == null) { + // these are lightweight objects and we don't care even if multiple objects ar ecreated b/c of + // a race condition + configs = lazyConfigMap(); + } + return configs; + } + + @Override + public String overseerNode() throws SolrException { + return null; + } + + @Override + public String thisNode() { + return null; + } + + private class SolrCollectionImpl implements SolrCollection { + final DocCollection coll; + final SimpleMap shards; + final ZkStateReader zkStateReader; + final Router router; + String confName; + + private SolrCollectionImpl(DocCollection coll, ZkStateReader zkStateReader) { + this.coll = coll; + this.zkStateReader = zkStateReader; + this.router = key -> coll.getRouter().getTargetSlice(key, null, null, null, null).getName(); + LinkedHashMap map = new LinkedHashMap<>(); + for (Slice slice : coll.getSlices()) { + map.put(slice.getName(), new ShardImpl(this, slice)); + } + shards = new WrappedSimpleMap<>(map); + } + + @Override + public String name() { + return coll.getName(); + } + + @Override + public SimpleMap shards() { + return shards; + } + + @Override + public String config() { + if (confName == null) { + // do this lazily . It's usually not necessary + try { + byte[] d = + zkStateReader + .getZkClient() + .getData(DocCollection.getCollectionPathRoot(coll.getName()), null, null, true); + if (d == null || d.length == 0) return null; + Map m = (Map) Utils.fromJSON(d); + confName = (String) m.get("configName"); + } catch (KeeperException | InterruptedException e) { + SimpleZkMap.throwZkExp(e); + // cannot read from ZK + return null; + } + } + return confName; + } + + @Override + public Router router() { + return router; + } + } + + private class ShardImpl implements Shard { + final SolrCollectionImpl collection; + final Slice slice; + final HashRange range; + final SimpleMap replicas; + + private ShardImpl(SolrCollectionImpl collection, Slice slice) { + this.collection = collection; + this.slice = slice; + range = _range(slice); + replicas = _replicas(); + } + + private SimpleMap _replicas() { + Map replicas = new HashMap<>(); + slice.forEach( + replica -> + replicas.put(replica.getName(), new ShardReplicaImpl(ShardImpl.this, replica))); + return new WrappedSimpleMap<>(replicas); + } + + private HashRange _range(Slice slice) { + return slice.getRange() == null + ? null + : new HashRange() { + @Override + public int min() { + return slice.getRange().min; + } + + @Override + public int max() { + return slice.getRange().max; + } + }; + } + + @Override + public String name() { + return slice.getName(); + } + + @Override + public String collection() { + return collection.name(); + } + + @Override + public HashRange range() { + return range; + } + + @Override + public SimpleMap replicas() { + return replicas; + } + + @Override + public String leader() { + Replica leader = slice.getLeader(); + return leader == null ? null : leader.getName(); + } + } + + private class ShardReplicaImpl implements ShardReplica { + private final ShardImpl shard; + private final Replica replica; + + private ShardReplicaImpl(ShardImpl shard, Replica replica) { + this.shard = shard; + this.replica = replica; + } + + @Override + public String name() { + return replica.getName(); + } + + @Override + public String shard() { + return shard.name(); + } + + @Override + public String collection() { + return shard.collection.name(); + } + + @Override + public String node() { + return replica.getNodeName(); + } + + @Override + public String core() { + return replica.getCoreName(); + } + + @Override + public Replica.Type type() { + return replica.getType(); + } + + @Override + public boolean alive() { + return zkStateReader.getClusterState().getLiveNodes().contains(node()) + && replica.getState() == Replica.State.ACTIVE; + } + + @Override + public long indexSize() { + // todo implement later + throw new UnsupportedOperationException("Not yet implemented"); + } + + @Override + public boolean isLeader() { + return Objects.equals(shard.leader(), name()); + } + + @Override + public String url(ApiType type) { + String base = nodes.get(node()).baseUrl(type); + if (type == ApiType.V2) { + return base + "/cores/" + core(); + } else { + return base + "/" + core(); + } + } + } + + private class Node implements SolrNode { + private final String name; + + private Node(String name) { + this.name = name; + } + + @Override + public String name() { + return name; + } + + @Override + public String baseUrl(ApiType apiType) { + return Utils.getBaseUrlForNodeName( + name, zkStateReader.getClusterProperty(URL_SCHEME, "http"), apiType == ApiType.V2); + } + + @Override + public SimpleMap cores() { + // todo implement later + // this requires a call to the node + throw new UnsupportedOperationException("Not yet implemented"); + } + } + + private class ConfigImpl implements CollectionConfig { + final String name; + final SimpleMap resources; + final String path; + + private ConfigImpl(String name) { + this.name = name; + path = ZkStateReader.CONFIGS_ZKNODE + "/" + name; + this.resources = new SimpleZkMap(zkStateReader, path); + } + + @Override + public SimpleMap resources() { + return resources; + } + + @Override + public String name() { + return name; + } + } +} diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java new file mode 100644 index 00000000000..f72d487256d --- /dev/null +++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java @@ -0,0 +1,137 @@ +/* + * 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.common; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import org.apache.solr.cluster.api.Resource; +import org.apache.solr.cluster.api.SimpleMap; +import org.apache.solr.common.cloud.ZkStateReader; +import org.apache.zookeeper.KeeperException; + +/** + * A view of ZK as a {@link SimpleMap} impl. This gives a flat view of all paths instead of a tree + * view eg: a, b, c , a/a1, a/a2, a/a1/aa1 etc If possible, use the {@link + * #abortableForEach(BiFunction)} to traverse DO not use the {@link #size()} method. It always + * return 0 because it is very expensive to compute that + */ +@Deprecated +public class SimpleZkMap implements SimpleMap { + private final ZkStateReader zkStateReader; + private final String basePath; + + static final byte[] EMPTY_BYTES = new byte[0]; + + public SimpleZkMap(ZkStateReader zkStateReader, String path) { + this.zkStateReader = zkStateReader; + this.basePath = path; + } + + @Override + public Resource get(String key) { + return readZkNode(basePath + key); + } + + @Override + public void abortableForEach(BiFunction fun) { + try { + recursiveRead("", zkStateReader.getZkClient().getChildren(basePath, null, true), fun); + } catch (KeeperException | InterruptedException e) { + throwZkExp(e); + } + } + + @Override + public void forEachEntry(BiConsumer fun) { + abortableForEach( + (path, resource) -> { + fun.accept(path, resource); + return Boolean.TRUE; + }); + } + + @Override + public int size() { + return 0; + } + + private Resource readZkNode(String path) { + return new Resource() { + @Override + public String name() { + return path; + } + + @Override + public void get(Consumer consumer) throws SolrException { + try { + byte[] data = + zkStateReader.getZkClient().getData(basePath + "/" + path, null, null, true); + if (data != null && data.length > 0) { + consumer.read(new ByteArrayInputStream(data)); + } else { + consumer.read(new ByteArrayInputStream(EMPTY_BYTES)); + } + } catch (KeeperException | InterruptedException e) { + throwZkExp(e); + } catch (IOException e) { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Can;t read stream", e); + } + } + }; + } + + private boolean recursiveRead( + String parent, List childrenList, BiFunction fun) { + if (childrenList == null || childrenList.isEmpty()) return true; + try { + Map> withKids = new LinkedHashMap<>(); + for (String child : childrenList) { + String relativePath = parent.isEmpty() ? child : parent + "/" + child; + if (!fun.apply(relativePath, readZkNode(relativePath))) return false; + List l1 = + zkStateReader.getZkClient().getChildren(basePath + "/" + relativePath, null, true); + if (l1 != null && !l1.isEmpty()) { + withKids.put(relativePath, l1); + } + } + // now we iterate through all nodes with sub paths + for (Map.Entry> e : withKids.entrySet()) { + // has children + if (!recursiveRead(e.getKey(), e.getValue(), fun)) { + return false; + } + } + } catch (KeeperException | InterruptedException e) { + throwZkExp(e); + } + return true; + } + + static void throwZkExp(Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "ZK errror", e); + } +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java b/solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java new file mode 100644 index 00000000000..1945a2039a1 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java @@ -0,0 +1,30 @@ +/* + * 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.cluster.api; + +/** Types of API calls */ +@Deprecated +public enum ApiType { + V1("solr"), + V2("api"); + final String prefix; + + ApiType(String prefix) { + this.prefix = prefix; + } +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/CollectionConfig.java b/solr/solrj/src/java/org/apache/solr/cluster/api/CollectionConfig.java new file mode 100644 index 00000000000..c3043ab50dc --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/CollectionConfig.java @@ -0,0 +1,26 @@ +/* + * 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.cluster.api; + +@Deprecated +public interface CollectionConfig { + + String name(); + + SimpleMap resources(); +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java b/solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java new file mode 100644 index 00000000000..3bfa66eaab6 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java @@ -0,0 +1,39 @@ +/* + * 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.cluster.api; + +/** A range of hash that is stored in a shard */ +@Deprecated +public interface HashRange { + + /** minimum value (inclusive) */ + int min(); + + /** maximum value (inclusive) */ + int max(); + + /** Check if a given hash falls in this range */ + default boolean includes(int hash) { + return hash >= min() && hash <= max(); + } + + /** Check if another range is a subset of this range */ + default boolean isSubset(HashRange subset) { + return min() <= subset.min() && max() >= subset.max(); + } +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java b/solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java new file mode 100644 index 00000000000..faa5207838b --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java @@ -0,0 +1,42 @@ +/* + * 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.cluster.api; + +import java.io.IOException; +import java.io.InputStream; +import org.apache.solr.common.SolrException; + +/** A binary resource. The impl is agnostic of the content type */ +@Deprecated +public interface Resource { + /** This is a full path. e.g schema.xml, solrconfig.xml, lang/stopwords.txt etc */ + String name(); + + /** + * read a file/resource. The caller should consume the stream completely and should not hold a + * reference to this stream. This method closes the stream soon after the method returns + * + * @param resourceConsumer This should be a full path. e.g schema.xml, solrconfig.xml, + * lang/stopwords.txt etc + */ + void get(Consumer resourceConsumer) throws SolrException; + + interface Consumer { + void read(InputStream is) throws IOException; + } +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/Router.java b/solr/solrj/src/java/org/apache/solr/cluster/api/Router.java new file mode 100644 index 00000000000..ecea98572f2 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/Router.java @@ -0,0 +1,26 @@ +/* + * 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.cluster.api; + +/** identify shards for a given routing key or document id */ +@Deprecated +public interface Router { + + /** shard name for a given routing key */ + String shard(String routingKey); +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java b/solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java new file mode 100644 index 00000000000..896cf8dd6b4 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java @@ -0,0 +1,38 @@ +/* + * 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.cluster.api; + +/** A shard of a collection */ +@Deprecated +public interface Shard { + + /** name of the shard */ + String name(); + + /** collection this shard belongs to */ + String collection(); + + /** hash range of this shard. null if this is not using hash based router */ + HashRange range(); + + /** replicas of the shard */ + SimpleMap replicas(); + + /** Name of the replica that is acting as the leader at the moment */ + String leader(); +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java b/solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java new file mode 100644 index 00000000000..0e85e51cea7 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java @@ -0,0 +1,58 @@ +/* + * 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.cluster.api; + +import org.apache.solr.common.cloud.Replica; + +/** replica of a shard */ +@Deprecated +public interface ShardReplica { + /** Name of this replica */ + String name(); + + /** The shard which it belongs to */ + String shard(); + + /** collection which it belongs to */ + String collection(); + + /** Name of the node where this replica is present */ + String node(); + + /** Name of the core where this is hosted */ + String core(); + + /** type of the replica */ + Replica.Type type(); + + /** Is the replica alive now */ + boolean alive(); + + /** + * Size of the index in bytes. Keep in mind that this may result in a network call. Also keep in + * mind that the value that you get is at best an approximation. The exact size may vary from + * replica to replica + */ + long indexSize(); + + /** Is this replica the leader */ + boolean isLeader(); + + /** Baseurl for this replica */ + String url(ApiType type); +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java new file mode 100644 index 00000000000..3684a2862df --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java @@ -0,0 +1,110 @@ +/* + * 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.cluster.api; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import org.apache.solr.common.MapWriter; + +/** + * A simplified read-only key-value structure. It is designed to support large datasets without + * consuming a lot of memory The objective is to provide implementations that are cheap and memory + * efficient to implement and consume. The keys are always {@link CharSequence} objects, The values + * can be of any type + */ +@Deprecated +public interface SimpleMap extends MapWriter { + + /** get a value by key. If not present , null is returned */ + T get(String key); + + @Deprecated + default T get(String key, T def) { + T val = get(key); + return val == null ? def : val; + } + + /** Navigate through all keys and values */ + @Deprecated + void forEachEntry(BiConsumer fun); + + /** + * iterate through all keys The default impl is suboptimal. Proper implementations must do it more + * efficiently + */ + @Deprecated + default void forEachKey(Consumer fun) { + forEachEntry((k, t) -> fun.accept(k)); + } + + int size(); + + /** + * iterate through all keys but abort in between if required The default impl is suboptimal. + * Proper implementations must do it more efficiently + * + * @param fun Consume each key and return a boolean to signal whether to proceed or not. If true, + * continue. If false stop + */ + @Deprecated + default void abortableForEachKey(Function fun) { + abortableForEach((key, t) -> fun.apply(key)); + } + + /** + * Navigate through all key-values but abort in between if required. The default impl is + * suboptimal. Proper implementations must do it more efficiently + * + * @param fun Consume each entry and return a boolean to signal whether to proceed or not. If + * true, continue, if false stop + */ + @Deprecated + default void abortableForEach(BiFunction fun) { + forEachEntry( + new BiConsumer<>() { + boolean end = false; + + @Override + public void accept(String k, T v) { + if (end) return; + end = fun.apply(k, v); + } + }); + } + + @Override + default void writeMap(EntryWriter ew) throws IOException { + forEachEntry(ew::putNoEx); + } + + @Deprecated + default Map asMap(Map sink) { + forEachEntry(sink::put); + return sink; + } + + @Deprecated + default Map asMap() { + return asMap(new LinkedHashMap<>()); + } +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java new file mode 100644 index 00000000000..1fa77d24c8d --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java @@ -0,0 +1,46 @@ +/* + * 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.cluster.api; + +import org.apache.solr.common.SolrException; + +/** Represents a Solr cluster */ +@Deprecated +public interface SolrCluster { + + /** collections in the cluster */ + SimpleMap collections() throws SolrException; + + /** collections in the cluster and aliases */ + SimpleMap collections(boolean includeAlias) throws SolrException; + + /** nodes in the cluster */ + SimpleMap nodes() throws SolrException; + + /** Config sets in the cluster */ + SimpleMap configs() throws SolrException; + + /** Name of the node in which the overseer is running */ + String overseerNode() throws SolrException; + + /** + * The name of the node in which this method is invoked from. returns null, if this is not invoked + * from a Solr node + */ + String thisNode(); +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCollection.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCollection.java new file mode 100644 index 00000000000..c0dd86d047a --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrCollection.java @@ -0,0 +1,34 @@ +/* + * 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.cluster.api; + +/** Represents a collection in Solr */ +@Deprecated +public interface SolrCollection { + + String name(); + + /** shards of a collection */ + SimpleMap shards(); + + /** Name of the configset used by this collection */ + String config(); + + /** Router used in this collection */ + Router router(); +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java new file mode 100644 index 00000000000..e6db68a1470 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java @@ -0,0 +1,35 @@ +/* + * 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.cluster.api; + +/** A read only view of a Solr node */ +@Deprecated +public interface SolrNode { + + /** The node name */ + String name(); + + /** Base http url for this node */ + String baseUrl(ApiType type); + + /** + * Get all the cores in a given node. This usually involves a network call. So, it's likely to be + * expensive + */ + SimpleMap cores(); +} diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java b/solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java new file mode 100644 index 00000000000..84e7f43b356 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** API interfaces for core SolrCloud classes */ +@Deprecated +package org.apache.solr.cluster.api; diff --git a/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java b/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java new file mode 100644 index 00000000000..768fef788b9 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java @@ -0,0 +1,58 @@ +/* + * 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.common.util; + +import java.util.Collections; +import java.util.Map; +import java.util.function.BiConsumer; +import org.apache.solr.cluster.api.SimpleMap; + +@Deprecated +public class WrappedSimpleMap implements SimpleMap { + private final Map delegate; + + @Override + public T get(String key) { + return delegate.get(key); + } + + @Override + public void forEachEntry(BiConsumer fun) { + delegate.forEach(fun); + } + + @Override + public int size() { + return delegate.size(); + } + + public WrappedSimpleMap(Map delegate) { + this.delegate = delegate; + } + + @Override + public Map asMap(Map sink) { + sink.putAll(delegate); + return sink; + } + + @Override + public Map asMap() { + return Collections.unmodifiableMap(delegate); + } +} From 24efa78ebf35dbf692d8fb588cceb1bea3e73118 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Tue, 10 Dec 2024 01:08:43 -0500 Subject: [PATCH 7/7] Keep NamedList implementing SimpleMap in 9x --- solr/CHANGES.txt | 4 ++ .../java/org/apache/solr/cli/DeleteTool.java | 2 +- .../apache/solr/common/util/NamedList.java | 37 ++++++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 2fec97d6129..3d5a809b6d2 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -271,6 +271,10 @@ led to the suppression of exceptions. (Andrey Bozhko) * SOLR-17577: Remove "solr.indexfetcher.sotimeout" system property that was for optimizing replication tests. It was disabled, but not removed. (Eric Pugh) +* SOLR-14680: NamedList: deprecating methods: forEachEntry, forEachKey, abortableForEachKey, abortableForEach, + asMap (no-arg only), get(key, default). Added getOrDefault. Deprecated the SimpleMap interface as well as the + entirety of the SolrJ package org.apache.solr.cluster.api, which wasn't used except for SimpleMap. (David Smiley) + ================== 9.7.1 ================== Bug Fixes --------------------- diff --git a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java index 7821a0d7ef0..c06e5ddf63c 100644 --- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java +++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java @@ -211,7 +211,7 @@ protected void deleteCollection(CloudSolrClient cloudSolrClient, CommandLine cli if (isVerbose() && response != null) { // pretty-print the response to stdout CharArr arr = new CharArr(); - new JSONWriter(arr, 2).write(response.asShallowMap()); + new JSONWriter(arr, 2).write(response.asMap(10)); echo(arr.toString()); echo("\n"); } diff --git a/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java b/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java index 46a08866d62..123a5e81d91 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java @@ -30,6 +30,10 @@ import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import org.apache.solr.cluster.api.SimpleMap; import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.MultiMapSolrParams; @@ -57,7 +61,7 @@ */ @SuppressWarnings({"unchecked", "rawtypes"}) public class NamedList - implements Cloneable, Serializable, Iterable>, MapWriter { + implements Cloneable, Serializable, Iterable>, MapWriter, SimpleMap { private static final long serialVersionUID = 1957981902839867821L; protected final List nvPairs; @@ -152,6 +156,7 @@ private List nameValueMapToList(Map.Entry[] nameVal } /** The total number of name/value pairs */ + @Override public int size() { return nvPairs.size() >> 1; } @@ -243,6 +248,7 @@ public int indexOf(String name, int start) { * @see #indexOf * @see #get(String,int) */ + @Override public T get(String name) { return get(name, 0); } @@ -815,6 +821,30 @@ public boolean equals(Object obj) { return this.nvPairs.equals(nl.nvPairs); } + @Override + public void abortableForEach(BiFunction fun) { + int sz = size(); + for (int i = 0; i < sz; i++) { + if (!fun.apply(getName(i), getVal(i))) break; + } + } + + @Override + public void abortableForEachKey(Function fun) { + int sz = size(); + for (int i = 0; i < sz; i++) { + if (!fun.apply(getName(i))) break; + } + } + + @Override + public void forEachKey(Consumer fun) { + int sz = size(); + for (int i = 0; i < sz; i++) { + fun.accept(getName(i)); + } + } + public void forEach(BiConsumer action) { int sz = size(); for (int i = 0; i < sz; i++) { @@ -826,4 +856,9 @@ public void forEach(BiConsumer action) { public int _size() { return size(); } + + @Override + public void forEachEntry(BiConsumer fun) { + forEach(fun); + } }