Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOLR-14680: Remove SimpleMap (affects NamedList) #2856

Merged
merged 9 commits into from
Dec 10, 2024
2 changes: 1 addition & 1 deletion solr/core/src/java/org/apache/solr/cli/ApiTool.java
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ protected String callGet(String url, String credentials) throws Exception {
NamedList<Object> 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.asMap(10));
return arr.toString();
}
}
10 changes: 4 additions & 6 deletions solr/core/src/java/org/apache/solr/cli/CreateTool.java
Original file line number Diff line number Diff line change
@@ -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<String, Object> systemInfo =
solrClient
.request(new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH))
.asMap();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needless asMap call. @epugh you added this code a year ago, maybe because you don't like NamedList?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right that I don't like NamedList, it's always been a weird Solr specific data structure. Why did we feel the need to come up with a unique data structure that isn't in the JDK? Having said that, what you are proposing is clearly an improvement!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wish we just had a cleaner data structure for these GSR that return JSON. I love that in our tests we have assertJQ and JQ methods that lets us grab some json. In a perfect world, this call would have returned a JSON datastructure, and I would use a Json query line to pluck out the core_root. This would be more compelling if we had a much more nested Map data structure to go through!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @gerlowskija would argue that the "perfect world" (we're slowly iterating towards) would not be JSON at this level of abstraction, it'd be a a machine-generated POJO generated from our OpenAPI that you call normal typed methods on. Correct me if I'm wrong Jason; I agree with that vision too.

But in the meantime Eric, get to know NamedList. It's not going anywhere anytime soon, and it's also not hard to use IMO. I wish it had the convenience methods that SolrParams has but not a big deal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to the POJO! And yep.

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.asMap(10));
echo(arr.toString());
}
String endMessage =
2 changes: 1 addition & 1 deletion solr/core/src/java/org/apache/solr/cli/DeleteTool.java
Original file line number Diff line number Diff line change
@@ -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");
}
2 changes: 1 addition & 1 deletion solr/core/src/java/org/apache/solr/cli/SolrCLI.java
Original file line number Diff line number Diff line change
@@ -705,7 +705,7 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin
Map<String, Object> failureStatus =
(Map<String, Object>) 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);
5 changes: 3 additions & 2 deletions solr/core/src/java/org/apache/solr/cli/StatusTool.java
Original file line number Diff line number Diff line change
@@ -358,13 +358,14 @@ protected Map<String, String> getCloudStatus(SolrClient solrClient, String zkHos
Map<String, String> cloudStatus = new LinkedHashMap<>();
cloudStatus.put("ZooKeeper", (zkHost != null) ? zkHost : "?");

// TODO add booleans to request just what we want; not everything
NamedList<Object> json = solrClient.request(new CollectionAdminRequest.ClusterStatus());

List<String> liveNodes = (List<String>) json.findRecursive("cluster", "live_nodes");
cloudStatus.put("liveNodes", String.valueOf(liveNodes.size()));

Map<String, Object> collections =
((NamedList<Object>) json.findRecursive("cluster", "collections")).asMap();
// TODO get this as a metric from the metrics API instead, or something else.
var collections = (NamedList<Object>) json.findRecursive("cluster", "collections");
cloudStatus.put("collections", String.valueOf(collections.size()));

return cloudStatus;
Original file line number Diff line number Diff line change
@@ -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<String> attributes() {
public Map<String, String> attributes() {
return delegate.attributes();
}

2 changes: 1 addition & 1 deletion solr/core/src/java/org/apache/solr/core/PluginInfo.java
Original file line number Diff line number Diff line change
@@ -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;
}
7 changes: 4 additions & 3 deletions solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
Original file line number Diff line number Diff line change
@@ -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<String, String> coreAdminHandlerActions =
readNodeListAsNamedList(root.get("coreAdminHandlerActions"), "<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
2 changes: 1 addition & 1 deletion solr/core/src/java/org/apache/solr/schema/IndexSchema.java
Original file line number Diff line number Diff line change
@@ -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 =
4 changes: 2 additions & 2 deletions solr/core/src/java/org/apache/solr/search/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ public static Map<String, CacheConfig> 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(
3 changes: 2 additions & 1 deletion solr/core/src/java/org/apache/solr/update/UpdateLog.java
Original file line number Diff line number Diff line change
@@ -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);

11 changes: 3 additions & 8 deletions solr/core/src/java/org/apache/solr/util/DOMConfigNode.java
Original file line number Diff line number Diff line change
@@ -18,22 +18,19 @@
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;

/** Read using DOM */
public class DOMConfigNode implements ConfigNode {

private final Node node;
SimpleMap<String> attrs;
Map<String, String> attrs;

@Override
public String name() {
@@ -50,10 +47,10 @@ public DOMConfigNode(Node node) {
}

@Override
public SimpleMap<String> attributes() {
public Map<String, String> attributes() {
if (attrs != null) return attrs;
Map<String, String> 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<ConfigNode, Boolean> fun) {
if (Boolean.FALSE.equals(toContinue)) break;
}
}

private static final SimpleMap<String> EMPTY = new WrappedSimpleMap<>(Collections.emptyMap());
}
116 changes: 85 additions & 31 deletions solr/core/src/java/org/apache/solr/util/DataConfigNode.java
Original file line number Diff line number Diff line change
@@ -17,31 +17,31 @@

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<String> attributes;
public final SimpleMap<List<ConfigNode>> kids;
public final Map<String, String> attributes;
public final Map<String, List<ConfigNode>> kids;
public final String textData;

public DataConfigNode(ConfigNode root) {
Map<String, List<ConfigNode>> kids = new LinkedHashMap<>();
name = root.name();
attributes = wrap(root.attributes());
attributes = wrapSubstituting(root.attributes());
textData = root.txt();
root.forEachChild(
it -> {
@@ -54,31 +54,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());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This particular aspect of the design of ConfigNode framework is, um, ... really concerning to me: a ThreadLocal to hold the substitution rules. Wow, so depending on which thread is looking(using) the map, they get different answers?! @noblepaul can you offer an explanation as to why it's this way instead of a normal field on the ConfigNode? This design has me concerned with respect to this PR since there were some Map conversions (via asMap which surprisingly creates a new Map; isn't a view) that maybe are now a view; I need to check. The difference is that previously the value would be materialized using the ThreadLocal of whoever called asMap but now would be whoever is looking at the values. Subtle!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was probbaly done to avoid changing the public APIs. We can change them now, not a big deal

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re were some Map conversions (via asMap which surprisingly creates a new Map;

YEs. it has to be changed. But asMap is not used in many places

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay; I'll file a pair of JIRA issues tomorrow if you don't first:

  • Replace ConfigNode.SUBSTITUTES (ThreadLocal); redesign
  • Rename NamedList.asMap to something like toMapRecursively

}

private SimpleMap<String> wrap(SimpleMap<String> delegate) {
/** provides a substitute view, and read-only */
private static Map<String, String> wrapSubstituting(Map<String, String> 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<String, ? super String> fun) {
delegate.forEachEntry((k, v) -> fun.accept(k, subtituteVal(v)));
}

@Override
public int size() {
return delegate.size();
}
};
return new SubstitutingMap(delegate);
}

@Override
@@ -88,11 +74,11 @@ public String name() {

@Override
public String txt() {
return subtituteVal(textData);
return substituteVal(textData);
}

@Override
public SimpleMap<String> attributes() {
public Map<String, String> attributes() {
return attributes;
}

@@ -104,7 +90,7 @@ public ConfigNode child(String name) {

@Override
public List<ConfigNode> getAll(String name) {
return kids.get(name, Collections.emptyList());
return kids.getOrDefault(name, List.of());
}

@Override
@@ -126,14 +112,82 @@ public List<ConfigNode> getAll(Predicate<ConfigNode> test, Set<String> matchName

@Override
public void forEachChild(Function<ConfigNode, Boolean> fun) {
kids.forEachEntry(
kids.forEach(
(s, configNodes) -> {
if (configNodes != null) {
configNodes.forEach(fun::apply);
}
});
}

public static final SimpleMap<List<ConfigNode>> EMPTY =
new WrappedSimpleMap<>(Collections.emptyMap());
private static class SubstitutingMap extends AbstractMap<String, String> {

private final Map<String, String> delegate;

SubstitutingMap(Map<String, String> 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<String> keySet() {
return delegate.keySet();
}

@Override
public void forEach(BiConsumer<? super String, ? super String> action) {
delegate.forEach((k, v) -> action.accept(k, substituteVal(v)));
}

@Override
public Set<Entry<String, String>> entrySet() {
return new AbstractSet<>() {
@Override
public Iterator<Entry<String, String>> iterator() {
// using delegate, return an iterator using Streams
return delegate.entrySet().stream()
.map(entry -> (Entry<String, String>) new SubstitutingEntry(entry))
.iterator();
}

@Override
public int size() {
return delegate.size();
}
};
}

private static class SubstitutingEntry implements Entry<String, String> {

private final Entry<String, String> delegateEntry;

SubstitutingEntry(Entry<String, String> 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();
}
}
}
}
104 changes: 0 additions & 104 deletions solr/core/src/test/org/apache/solr/cloud/TestLazySolrCluster.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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();
Original file line number Diff line number Diff line change
@@ -90,11 +90,10 @@ public void testAggregateNodeLevelMetrics() throws SolrServerException, IOExcept
final double[] minUpdateTime = {Double.MAX_VALUE};
final double[] maxUpdateTime = {-1.0};
Set<NamedList<Object>> coreMetrics = new HashSet<>();
metrics.forEachKey(
(key) -> {
metrics.forEach(
(key, coreMetric) -> {
if (key.startsWith("solr.core.testRequestHandlerMetrics")) {
NamedList<Object> coreMetric = (NamedList<Object>) metrics.get(key);
coreMetrics.add(coreMetric);
coreMetrics.add((NamedList<Object>) coreMetric);
}
});
assertEquals(2, coreMetrics.size());
Original file line number Diff line number Diff line change
@@ -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<String, Object> actionResponse = ((NamedList<Object>) response.getResponse()).asMap();
var actionResponse = (NamedList<Object>) 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,
Original file line number Diff line number Diff line change
@@ -155,9 +155,11 @@ public void testClassicFacets() throws Exception { // AKA SimpleFacets
.findRecursive("facet_counts", "facet_heatmaps", "course", "gridLevel"));
assertTrue(
((NamedList<Object>)
response.getResponse().findRecursive("facet_counts", "facet_heatmaps", "course"))
.asMap(0)
.containsKey("counts_" + courseFormat));
response
.getResponse()
.findRecursive("facet_counts", "facet_heatmaps", "course"))
.indexOf("counts_" + courseFormat, 0)
>= 0);
}

// ------ Index data

This file was deleted.

136 changes: 0 additions & 136 deletions solr/solrj-zookeeper/src/java/org/apache/solr/common/SimpleZkMap.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -212,7 +212,7 @@ public static PerReplicaStatesOps disable(PerReplicaStates rs) {
new PerReplicaStatesOps(
prs -> {
List<PerReplicaStates.Operation> result = new ArrayList<>();
prs.states.forEachEntry(
prs.states.forEach(
(s, state) ->
result.add(
new PerReplicaStates.Operation(
29 changes: 0 additions & 29 deletions solr/solrj/src/java/org/apache/solr/cluster/api/ApiType.java

This file was deleted.

This file was deleted.

38 changes: 0 additions & 38 deletions solr/solrj/src/java/org/apache/solr/cluster/api/HashRange.java

This file was deleted.

41 changes: 0 additions & 41 deletions solr/solrj/src/java/org/apache/solr/cluster/api/Resource.java

This file was deleted.

25 changes: 0 additions & 25 deletions solr/solrj/src/java/org/apache/solr/cluster/api/Router.java

This file was deleted.

37 changes: 0 additions & 37 deletions solr/solrj/src/java/org/apache/solr/cluster/api/Shard.java

This file was deleted.

57 changes: 0 additions & 57 deletions solr/solrj/src/java/org/apache/solr/cluster/api/ShardReplica.java

This file was deleted.

102 changes: 0 additions & 102 deletions solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java

This file was deleted.

45 changes: 0 additions & 45 deletions solr/solrj/src/java/org/apache/solr/cluster/api/SolrCluster.java

This file was deleted.

This file was deleted.

34 changes: 0 additions & 34 deletions solr/solrj/src/java/org/apache/solr/cluster/api/SolrNode.java

This file was deleted.

19 changes: 0 additions & 19 deletions solr/solrj/src/java/org/apache/solr/cluster/api/package-info.java

This file was deleted.

11 changes: 4 additions & 7 deletions solr/solrj/src/java/org/apache/solr/common/ConfigNode.java
Original file line number Diff line number Diff line change
@@ -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<String> attributes();
Map<String, String> attributes();

/** Child by name */
default ConfigNode child(String name) {
@@ -195,8 +194,8 @@ public String txt() {
}

@Override
public SimpleMap<String> attributes() {
return empty_attrs;
public Map<String, String> attributes() {
return Map.of();
}

@Override
@@ -233,8 +232,6 @@ public boolean isNull() {
public void forEachChild(Function<ConfigNode, Boolean> fun) {}
};

SimpleMap<String> empty_attrs = new WrappedSimpleMap<>(Collections.emptyMap());

class Helpers {
static boolean _bool(Object v, boolean def) {
return v == null ? def : Boolean.parseBoolean(v.toString());
Original file line number Diff line number Diff line change
@@ -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<String, Object> 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<Range>, HashRange {
public static class Range implements JSONWriter.Writable, Comparable<Range> {
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;
}
Original file line number Diff line number Diff line change
@@ -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<State> states;
@JsonProperty public final Map<String, State> states;

private volatile Boolean allActive;

@@ -85,7 +83,7 @@ public PerReplicaStates(String path, int cversion, List<String> 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<String> findModifiedReplicas(PerReplicaStates old, PerReplicaStates fresh) {
Set<String> 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<String, State>() {
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);
}
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ public static Map<String, String> toMap(ConfigNode node) {
public static Map<String, String> toMapExcept(ConfigNode node, String... exclusions) {
Map<String, String> args = new HashMap<>();
node.attributes()
.forEachEntry(
.forEach(
(k, v) -> {
for (String ex : exclusions) if (ex.equals(k)) return;
args.put(k, v);
45 changes: 8 additions & 37 deletions solr/solrj/src/java/org/apache/solr/common/util/NamedList.java
Original file line number Diff line number Diff line change
@@ -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<T>
implements Cloneable, Serializable, Iterable<Map.Entry<String, T>>, MapWriter, SimpleMap<T> {
implements Cloneable, Serializable, Iterable<Map.Entry<String, T>>, MapWriter {

private static final long serialVersionUID = 1957981902839867821L;
protected final List<Object> nvPairs;
@@ -156,7 +152,6 @@ private List<Object> nameValueMapToList(Map.Entry<String, ? extends T>[] 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<Entry<String, T>> 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<String, ? super T, Boolean> fun) {
int sz = size();
for (int i = 0; i < sz; i++) {
if (!fun.apply(getName(i), getVal(i))) break;
}
}

@Override
public void abortableForEachKey(Function<String, Boolean> fun) {
int sz = size();
for (int i = 0; i < sz; i++) {
if (!fun.apply(getName(i))) break;
}
}

@Override
public void forEachKey(Consumer<String> fun) {
int sz = size();
for (int i = 0; i < sz; i++) {
fun.accept(getName(i));
}
}

public void forEach(BiConsumer<String, ? super T> action) {
int sz = size();
for (int i = 0; i < sz; i++) {
@@ -853,9 +829,4 @@ public void forEach(BiConsumer<String, ? super T> action) {
public int _size() {
return size();
}

@Override
public void forEachEntry(BiConsumer<String, ? super T> fun) {
forEach(fun);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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);
});