This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 340e604d0ed SOLR-16458: Migrate node properties API to JAX-RS (#4775)
340e604d0ed is described below
commit 340e604d0eda6dabebf572bd4656f4b9b273d27e
Author: Prithvi S <[email protected]>
AuthorDate: Fri Aug 28 17:20:08 2026 +0530
SOLR-16458: Migrate node properties API to JAX-RS (#4775)
Signed-off-by: prithvi <[email protected]>
Co-authored-by: Eric Pugh <[email protected]>
---
.../SOLR-16458-migrate-node-properties-api.yml | 8 ++
.../client/api/endpoint/NodePropertiesApi.java | 44 ++++++++
.../client/api/model/NodePropertiesResponse.java | 34 ++++++
.../handler/admin/PropertiesRequestHandler.java | 40 ++++---
.../solr/handler/admin/api/GetNodeProperties.java | 92 ++++++++++++++++
.../solr/handler/admin/api/NodePropertiesAPI.java | 47 --------
.../admin/PropertiesRequestHandlerTest.java | 40 ++++++-
.../handler/admin/api/GetNodePropertiesTest.java | 120 +++++++++++++++++++++
.../handler/admin/api/V2NodeAPIMappingTest.java | 24 -----
.../pages/implicit-requesthandlers.adoc | 9 +-
.../deployment-guide/pages/jvm-settings.adoc | 4 +
.../solr/client/solrj/impl/NodeValueFetcher.java | 16 ++-
12 files changed, 378 insertions(+), 100 deletions(-)
diff --git a/changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml
b/changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml
new file mode 100644
index 00000000000..0b45680bdec
--- /dev/null
+++ b/changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml
@@ -0,0 +1,8 @@
+title: "A single node system property can be fetched at GET
/api/node/properties/{propertyName} (v1 still uses ?name=). SolrJ now provides
NodeApi.GetNodeProperties and NodeApi.GetNodeProperty."
+type: added
+authors:
+ - name: Prithvi S
+ nick: iprithv
+links:
+ - name: SOLR-16458
+ url: https://issues.apache.org/jira/browse/SOLR-16458
diff --git
a/solr/api/src/java/org/apache/solr/client/api/endpoint/NodePropertiesApi.java
b/solr/api/src/java/org/apache/solr/client/api/endpoint/NodePropertiesApi.java
new file mode 100644
index 00000000000..7c986e5ad82
--- /dev/null
+++
b/solr/api/src/java/org/apache/solr/client/api/endpoint/NodePropertiesApi.java
@@ -0,0 +1,44 @@
+/*
+ * 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.client.api.endpoint;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import org.apache.solr.client.api.model.NodePropertiesResponse;
+
+/** V2 API definition for listing JRE system properties on a Solr node. */
+@Path("/node/properties")
+public interface NodePropertiesApi {
+
+ @GET
+ @Operation(
+ summary = "List system properties for the target Solr node.",
+ tags = {"node"})
+ NodePropertiesResponse getNodeProperties();
+
+ @GET
+ @Path("/{propertyName}")
+ @Operation(
+ summary = "Get a single system property for the target Solr node.",
+ tags = {"node"})
+ NodePropertiesResponse getNodeProperty(
+ @Parameter(description = "Name of the system property to return.")
@PathParam("propertyName")
+ String propertyName);
+}
diff --git
a/solr/api/src/java/org/apache/solr/client/api/model/NodePropertiesResponse.java
b/solr/api/src/java/org/apache/solr/client/api/model/NodePropertiesResponse.java
new file mode 100644
index 00000000000..81ca0331fdd
--- /dev/null
+++
b/solr/api/src/java/org/apache/solr/client/api/model/NodePropertiesResponse.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.client.api.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.Map;
+
+/**
+ * Response body for {@code GET /api/node/properties} and {@code GET
+ * /api/node/properties/{propertyName}}.
+ */
+public class NodePropertiesResponse extends SolrJerseyResponse {
+
+ public static final String SYSTEM_PROPERTIES = "system.properties";
+
+ @Schema(description = "JRE system properties for the Solr node. Secret
values are redacted.")
+ @JsonProperty(SYSTEM_PROPERTIES)
+ public Map<String, String> systemProperties;
+}
diff --git
a/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java
b/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java
index 8658adf3c52..fb290bdd1a2 100644
---
a/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java
+++
b/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java
@@ -20,20 +20,22 @@ import static
org.apache.solr.common.params.CommonParams.NAME;
import java.io.IOException;
import java.util.Collection;
-import java.util.Enumeration;
-import org.apache.solr.api.AnnotatedApi;
+import java.util.List;
import org.apache.solr.api.Api;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.api.JerseyResource;
+import org.apache.solr.client.api.model.NodePropertiesResponse;
import org.apache.solr.core.CoreContainer;
-import org.apache.solr.core.NodeConfig;
import org.apache.solr.handler.RequestHandlerBase;
-import org.apache.solr.handler.admin.api.NodePropertiesAPI;
+import org.apache.solr.handler.admin.api.GetNodeProperties;
+import org.apache.solr.handler.api.V2ApiUtils;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.AuthorizationContext;
/**
+ * v1 implementation of {@code GET /admin/info/properties}. Business logic
lives in {@link
+ * GetNodeProperties}.
+ *
* @since solr 1.2
*/
public class PropertiesRequestHandler extends RequestHandlerBase {
@@ -51,21 +53,12 @@ public class PropertiesRequestHandler extends
RequestHandlerBase {
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp)
throws IOException {
- NamedList<String> props = new SimpleOrderedMap<>();
- String name = req.getParams().get(NAME);
- NodeConfig nodeConfig = getCoreContainer(req).getNodeConfig();
- if (name != null) {
- String property = nodeConfig.getRedactedSysPropValue(name);
- props.add(name, property);
- } else {
- Enumeration<?> enumeration = System.getProperties().propertyNames();
- while (enumeration.hasMoreElements()) {
- name = (String) enumeration.nextElement();
- props.add(name, nodeConfig.getRedactedSysPropValue(name));
- }
- }
- rsp.add("system.properties", props);
rsp.setHttpCaching(false);
+ final GetNodeProperties api = new GetNodeProperties(getCoreContainer(req));
+ final NodePropertiesResponse response = new NodePropertiesResponse();
+ // v1 ?name= returns the key even if unset; the v2 path form 404s for
unknown names.
+ response.systemProperties =
api.collectProperties(req.getParams().get(NAME));
+ V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, response);
}
//////////////////////// SolrInfoMBeans methods //////////////////////
@@ -82,7 +75,12 @@ public class PropertiesRequestHandler extends
RequestHandlerBase {
@Override
public Collection<Api> getApis() {
- return AnnotatedApi.getApis(new NodePropertiesAPI(this));
+ return List.of();
+ }
+
+ @Override
+ public Collection<Class<? extends JerseyResource>> getJerseyResources() {
+ return List.of(GetNodeProperties.class);
}
@Override
diff --git
a/solr/core/src/java/org/apache/solr/handler/admin/api/GetNodeProperties.java
b/solr/core/src/java/org/apache/solr/handler/admin/api/GetNodeProperties.java
new file mode 100644
index 00000000000..464ec22c687
--- /dev/null
+++
b/solr/core/src/java/org/apache/solr/handler/admin/api/GetNodeProperties.java
@@ -0,0 +1,92 @@
+/*
+ * 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.handler.admin.api;
+
+import jakarta.inject.Inject;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.solr.api.JerseyResource;
+import org.apache.solr.client.api.endpoint.NodePropertiesApi;
+import org.apache.solr.client.api.model.NodePropertiesResponse;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.NodeConfig;
+import org.apache.solr.jersey.PermissionName;
+import org.apache.solr.security.PermissionNameProvider;
+
+/**
+ * V2 API for listing system properties on the receiving node.
+ *
+ * <p>GET /api/node/properties lists all properties. GET
/api/node/properties/{propertyName} returns
+ * a single property. Both are analogous to v1 /admin/info/properties, which
still uses a {@code
+ * name} query parameter for the single-property form.
+ *
+ * <p>The v1 {@link org.apache.solr.handler.admin.PropertiesRequestHandler}
delegates to this class.
+ */
+public class GetNodeProperties extends JerseyResource implements
NodePropertiesApi {
+
+ private final CoreContainer coreContainer;
+
+ @Inject
+ public GetNodeProperties(CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ @Override
+ @PermissionName(PermissionNameProvider.Name.CONFIG_READ_PERM)
+ public NodePropertiesResponse getNodeProperties() {
+ return buildResponse(null);
+ }
+
+ @Override
+ @PermissionName(PermissionNameProvider.Name.CONFIG_READ_PERM)
+ public NodePropertiesResponse getNodeProperty(String propertyName) {
+ final NodeConfig nodeConfig = coreContainer.getNodeConfig();
+ // Hidden names always return 200 + a redacted value, even if unset, so
callers cannot
+ // probe whether a secret is configured.
+ if (!System.getProperties().containsKey(propertyName)
+ && !nodeConfig.isSysPropHidden(propertyName)) {
+ throw new SolrException(
+ SolrException.ErrorCode.NOT_FOUND,
+ "No system property named '" + propertyName + "' exists on this
node.");
+ }
+ return buildResponse(propertyName);
+ }
+
+ private NodePropertiesResponse buildResponse(String name) {
+ final NodePropertiesResponse response =
instantiateJerseyResponse(NodePropertiesResponse.class);
+ response.systemProperties = collectProperties(name);
+ return response;
+ }
+
+ /** Collect redacted system properties, optionally limited to a single named
property. */
+ public Map<String, String> collectProperties(String name) {
+ final NodeConfig nodeConfig = coreContainer.getNodeConfig();
+ final Map<String, String> props = new LinkedHashMap<>();
+ if (name != null) {
+ props.put(name, nodeConfig.getRedactedSysPropValue(name));
+ } else {
+ Enumeration<?> enumeration = System.getProperties().propertyNames();
+ while (enumeration.hasMoreElements()) {
+ String propertyName = (String) enumeration.nextElement();
+ props.put(propertyName,
nodeConfig.getRedactedSysPropValue(propertyName));
+ }
+ }
+ return props;
+ }
+}
diff --git
a/solr/core/src/java/org/apache/solr/handler/admin/api/NodePropertiesAPI.java
b/solr/core/src/java/org/apache/solr/handler/admin/api/NodePropertiesAPI.java
deleted file mode 100644
index d9cf81f8a8b..00000000000
---
a/solr/core/src/java/org/apache/solr/handler/admin/api/NodePropertiesAPI.java
+++ /dev/null
@@ -1,47 +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.handler.admin.api;
-
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET;
-import static
org.apache.solr.security.PermissionNameProvider.Name.CONFIG_READ_PERM;
-
-import org.apache.solr.api.EndPoint;
-import org.apache.solr.handler.admin.PropertiesRequestHandler;
-import org.apache.solr.request.SolrQueryRequest;
-import org.apache.solr.response.SolrQueryResponse;
-
-/**
- * V2 API for listing system properties for each node.
- *
- * <p>This API (GET /v2/node/properties) is analogous to the v1
/admin/info/properties.
- */
-public class NodePropertiesAPI {
- private final PropertiesRequestHandler handler;
-
- public NodePropertiesAPI(PropertiesRequestHandler handler) {
- this.handler = handler;
- }
-
- @EndPoint(
- path = {"/node/properties"},
- method = GET,
- permission = CONFIG_READ_PERM)
- public void getRequestedProperties(SolrQueryRequest req, SolrQueryResponse
rsp) throws Exception {
- handler.handleRequestBody(req, rsp);
- }
-}
diff --git
a/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java
b/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java
index f64b96ae311..7202a6b5a54 100644
---
a/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java
+++
b/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java
@@ -16,11 +16,13 @@
*/
package org.apache.solr.handler.admin;
+import java.util.Map;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
+import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.NodeConfig;
import org.junit.BeforeClass;
@@ -45,7 +47,7 @@ public class PropertiesRequestHandlerTest extends
SolrTestCaseJ4 {
"some.Secret"
}) {
System.setProperty(propName, PASSWORD);
- NamedList<Object> properties = readProperties();
+ Map<String, Object> properties = readProperties();
assertEquals(
"Failed to redact " + propName,
@@ -54,13 +56,41 @@ public class PropertiesRequestHandlerTest extends
SolrTestCaseJ4 {
}
}
+ @Test
+ public void testSingleProperty() throws Exception {
+ System.setProperty("GetNodeProperties.v1.visible", "hello");
+ try {
+ Map<String, Object> properties =
readProperties("GetNodeProperties.v1.visible");
+ assertEquals(1, properties.size());
+ assertEquals("hello", properties.get("GetNodeProperties.v1.visible"));
+ } finally {
+ System.clearProperty("GetNodeProperties.v1.visible");
+ }
+ }
+
+ @Test
+ public void testMissingPropertyStillReturned() throws Exception {
+ Map<String, Object> properties =
readProperties("GetNodeProperties.v1.doesNotExist");
+ assertEquals(1, properties.size());
+ assertTrue(properties.containsKey("GetNodeProperties.v1.doesNotExist"));
+ assertNull(properties.get("GetNodeProperties.v1.doesNotExist"));
+ }
+
+ private Map<String, Object> readProperties() throws Exception {
+ return readProperties(null);
+ }
+
@SuppressWarnings({"unchecked"})
- private NamedList<Object> readProperties() throws Exception {
+ private Map<String, Object> readProperties(String name) throws Exception {
SolrClient client = new EmbeddedSolrServer(h.getCore());
-
+ ModifiableSolrParams params = new ModifiableSolrParams();
+ if (name != null) {
+ params.set("name", name);
+ }
NamedList<Object> properties =
- client.request(new GenericSolrRequest(SolrRequest.METHOD.GET,
"/admin/info/properties"));
+ client.request(
+ new GenericSolrRequest(SolrRequest.METHOD.GET,
"/admin/info/properties", params));
- return (NamedList<Object>) properties.get("system.properties");
+ return (Map<String, Object>) properties.get("system.properties");
}
}
diff --git
a/solr/core/src/test/org/apache/solr/handler/admin/api/GetNodePropertiesTest.java
b/solr/core/src/test/org/apache/solr/handler/admin/api/GetNodePropertiesTest.java
new file mode 100644
index 00000000000..c00a8822310
--- /dev/null
+++
b/solr/core/src/test/org/apache/solr/handler/admin/api/GetNodePropertiesTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.handler.admin.api;
+
+import java.util.Collections;
+import org.apache.solr.SolrTestCase;
+import org.apache.solr.client.api.model.NodePropertiesResponse;
+import org.apache.solr.client.solrj.RemoteSolrException;
+import org.apache.solr.client.solrj.request.NodeApi;
+import org.apache.solr.core.NodeConfig;
+import org.apache.solr.util.SolrJettyTestRule;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+/**
+ * HTTP tests for {@code GET /api/node/properties} and {@code GET
+ * /api/node/properties/{propertyName}} via the generated SolrJ client classes.
+ */
+public class GetNodePropertiesTest extends SolrTestCase {
+
+ private static final String VISIBLE_PROP = "GetNodePropertiesTest.visible";
+ private static final String SECRET_PROP = "GetNodePropertiesTest.password";
+ private static final String PASSWORD = "secret123";
+
+ @ClassRule public static final SolrJettyTestRule solrTestRule = new
SolrJettyTestRule();
+
+ @BeforeClass
+ public static void setupSolr() throws Exception {
+ solrTestRule.startSolr(createTempDir());
+ }
+
+ @After
+ public void clearTestProperties() {
+ System.clearProperty(VISIBLE_PROP);
+ System.clearProperty(SECRET_PROP);
+ }
+
+ @Test
+ public void testNamedProperty() throws Exception {
+ var req = new NodeApi.GetNodeProperty("java.version");
+ var rsp = req.process(solrTestRule.getAdminClient());
+
+ assertNotNull(rsp);
+ assertNull(rsp.error);
+ assertEquals(1, rsp.systemProperties.size());
+ assertEquals(System.getProperty("java.version"),
rsp.systemProperties.get("java.version"));
+ }
+
+ @Test
+ public void testAllProperties() throws Exception {
+ System.setProperty(VISIBLE_PROP, "hello");
+
+ NodePropertiesResponse rsp = fetchProperties(null);
+
+ assertEquals(
+ Collections.list(System.getProperties().propertyNames()).size(),
+ rsp.systemProperties.size());
+ assertEquals(System.getProperty("java.version"),
rsp.systemProperties.get("java.version"));
+ assertEquals("hello", rsp.systemProperties.get(VISIBLE_PROP));
+ }
+
+ @Test
+ public void testRedactsHiddenProperties() throws Exception {
+ System.setProperty(SECRET_PROP, PASSWORD);
+
+ NodePropertiesResponse named = fetchProperties(SECRET_PROP);
+ assertEquals(1, named.systemProperties.size());
+ assertEquals(NodeConfig.REDACTED_SYS_PROP_VALUE,
named.systemProperties.get(SECRET_PROP));
+ assertFalse(named.systemProperties.containsValue(PASSWORD));
+
+ NodePropertiesResponse all = fetchProperties(null);
+ assertEquals(NodeConfig.REDACTED_SYS_PROP_VALUE,
all.systemProperties.get(SECRET_PROP));
+ assertFalse(all.systemProperties.containsValue(PASSWORD));
+ }
+
+ @Test
+ public void testUnknownPropertyReturns404() {
+ var req = new
NodeApi.GetNodeProperty("GetNodePropertiesTest.doesNotExist");
+ final RemoteSolrException ex =
+ expectThrows(RemoteSolrException.class, () ->
req.process(solrTestRule.getAdminClient()));
+ assertEquals(404, ex.code());
+ }
+
+ @Test
+ public void testUnknownHiddenPropertyDoesNotRevealExistence() throws
Exception {
+ final String hiddenUnset = "GetNodePropertiesTest.doesNotExist.password";
+ assertFalse(System.getProperties().containsKey(hiddenUnset));
+
+ NodePropertiesResponse rsp = fetchProperties(hiddenUnset);
+ assertEquals(1, rsp.systemProperties.size());
+ assertEquals(NodeConfig.REDACTED_SYS_PROP_VALUE,
rsp.systemProperties.get(hiddenUnset));
+ }
+
+ private NodePropertiesResponse fetchProperties(String name) throws Exception
{
+ NodePropertiesResponse rsp =
+ name == null
+ ? new
NodeApi.GetNodeProperties().process(solrTestRule.getAdminClient())
+ : new
NodeApi.GetNodeProperty(name).process(solrTestRule.getAdminClient());
+ assertNotNull(rsp);
+ assertNull(rsp.error);
+ assertNotNull(rsp.systemProperties);
+ return rsp;
+ }
+}
diff --git
a/solr/core/src/test/org/apache/solr/handler/admin/api/V2NodeAPIMappingTest.java
b/solr/core/src/test/org/apache/solr/handler/admin/api/V2NodeAPIMappingTest.java
index 6b3c63de45b..ac2b660bc88 100644
---
a/solr/core/src/test/org/apache/solr/handler/admin/api/V2NodeAPIMappingTest.java
+++
b/solr/core/src/test/org/apache/solr/handler/admin/api/V2NodeAPIMappingTest.java
@@ -35,8 +35,6 @@ import org.apache.solr.common.util.ContentStreamBase;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.handler.admin.InfoHandler;
-import org.apache.solr.handler.admin.LoggingHandler;
-import org.apache.solr.handler.admin.PropertiesRequestHandler;
import org.apache.solr.handler.admin.ThreadDumpHandler;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryRequestBase;
@@ -52,8 +50,6 @@ public class V2NodeAPIMappingTest extends SolrTestCaseJ4 {
private ArgumentCaptor<SolrQueryRequest> queryRequestCaptor;
private CoreAdminHandler mockCoresHandler;
private InfoHandler infoHandler;
- private LoggingHandler mockLoggingHandler;
- private PropertiesRequestHandler mockPropertiesHandler;
private ThreadDumpHandler mockThreadDumpHandler;
@BeforeClass
@@ -65,13 +61,9 @@ public class V2NodeAPIMappingTest extends SolrTestCaseJ4 {
public void setupApiBag() {
mockCoresHandler = mock(CoreAdminHandler.class);
infoHandler = mock(InfoHandler.class);
- mockLoggingHandler = mock(LoggingHandler.class);
- mockPropertiesHandler = mock(PropertiesRequestHandler.class);
mockThreadDumpHandler = mock(ThreadDumpHandler.class);
queryRequestCaptor = ArgumentCaptor.forClass(SolrQueryRequest.class);
- when(infoHandler.getLoggingHandler()).thenReturn(mockLoggingHandler);
- when(infoHandler.getPropertiesHandler()).thenReturn(mockPropertiesHandler);
when(infoHandler.getThreadDumpHandler()).thenReturn(mockThreadDumpHandler);
apiBag = new ApiBag(false);
@@ -116,16 +108,6 @@ public class V2NodeAPIMappingTest extends SolrTestCaseJ4 {
assertEquals("true", v1Params.get("rejoinAtHead"));
}
- @Test
- public void testSystemPropsApiAllProperties() throws Exception {
- final ModifiableSolrParams solrParams = new ModifiableSolrParams();
- solrParams.add("name", "specificPropertyName");
- final SolrParams v1Params =
- captureConvertedPropertiesV1Params("/node/properties", "GET",
solrParams);
-
- assertEquals("specificPropertyName", v1Params.get("name"));
- }
-
@Test
public void testThreadDumpApiAllProperties() throws Exception {
final ModifiableSolrParams solrParams = new ModifiableSolrParams();
@@ -143,11 +125,6 @@ public class V2NodeAPIMappingTest extends SolrTestCaseJ4 {
path, method, new ModifiableSolrParams(), v2RequestBody,
mockCoresHandler);
}
- private SolrParams captureConvertedPropertiesV1Params(
- String path, String method, SolrParams inputParams) throws Exception {
- return doCaptureParams(path, method, inputParams, null,
mockPropertiesHandler);
- }
-
private SolrParams captureConvertedThreadDumpV1Params(
String path, String method, SolrParams inputParams) throws Exception {
return doCaptureParams(path, method, inputParams, null,
mockThreadDumpHandler);
@@ -188,7 +165,6 @@ public class V2NodeAPIMappingTest extends SolrTestCaseJ4 {
ApiBag apiBag, CoreAdminHandler coreHandler, InfoHandler infoHandler) {
apiBag.registerObject(new OverseerOperationAPI(coreHandler));
apiBag.registerObject(new RejoinLeaderElectionAPI(coreHandler));
- apiBag.registerObject(new
NodePropertiesAPI(infoHandler.getPropertiesHandler()));
apiBag.registerObject(new
NodeThreadsAPI(infoHandler.getThreadDumpHandler()));
}
}
diff --git
a/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc
b/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc
index 4380337752c..005742a7cec 100644
---
a/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc
+++
b/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc
@@ -92,14 +92,21 @@ This handler must have a collection name in the path to the
endpoint.
|===
System Properties:: Return JRE system properties.
+Secret values are redacted.
++
+*Documentation*:
xref:deployment-guide:jvm-settings.adoc#java-properties-screen[Java Properties
Screen]
+
[cols="3*.",frame=none,grid=cols,options="header"]
|===
|API Endpoints |Class & Javadocs |Paramset
|v1: `solr/admin/info/properties`
-v2: `api/node/properties`
|{solr-javadocs}/core/org/apache/solr/handler/admin/PropertiesRequestHandler.html[PropertiesRequestHandler]
|`_ADMIN_PROPERTIES`
+v2: `api/node/properties` |v1:
{solr-javadocs}/core/org/apache/solr/handler/admin/PropertiesRequestHandler.html[PropertiesRequestHandler]
+
+v2:
{solr-javadocs}/core/org/apache/solr/handler/admin/api/GetNodeProperties.html[GetNodeProperties]
|`_ADMIN_PROPERTIES`
|===
++
+To fetch a single property, v1 uses a `name` query parameter
(`solr/admin/info/properties?name=java.version`) and v2 uses a path segment
(`api/node/properties/java.version`).
Segments:: Return info on last commit generation Lucene index segments.
+
diff --git
a/solr/solr-ref-guide/modules/deployment-guide/pages/jvm-settings.adoc
b/solr/solr-ref-guide/modules/deployment-guide/pages/jvm-settings.adoc
index a40cdc4073e..5c9dc071163 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/jvm-settings.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/jvm-settings.adoc
@@ -72,6 +72,10 @@ Test this by running `java -help` and look for `-server` as
an available option
A great way to see what JVM settings your server is using, along with other
useful information, is to use the `admin` request handler,
`/solr/admin/info/system`.
This request handler will display a wealth of server statistics and settings.
+The JVM's system properties are available from `GET
/solr/admin/info/properties` (v1) or `GET /api/node/properties` (v2).
+A single property can be requested with v1 `?name=java.version` or v2 `GET
/api/node/properties/java.version`.
+Secret values are redacted.
+
=== Java Properties Screen
Many of the system environment variables include Java settings, and these can
be seen on the main Dashboard of the Admin UI.
diff --git
a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java
b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java
index abd31c5d86c..917eb59ceea 100644
---
a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java
+++
b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java
@@ -26,6 +26,7 @@ import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -243,9 +244,10 @@ public class NodeValueFetcher {
ModifiableSolrParams params = new ModifiableSolrParams();
try {
SimpleSolrResponse rsp = ctx.invokeWithRetry(ctx.getNode(),
"/admin/info/properties", params);
- NamedList<?> systemPropsRsp = (NamedList<?>)
rsp.getResponse().get("system.properties");
+ Object systemPropsRsp = rsp.getResponse().get("system.properties");
for (String requestedProperty : requestedTagNames) {
- Object property =
systemPropsRsp.get(requestedProperty.substring(SYSPROP_PREFIX.length()));
+ String key = requestedProperty.substring(SYSPROP_PREFIX.length());
+ Object property = getSysProp(systemPropsRsp, key);
if (property != null) ctx.tags.put(requestedProperty,
property.toString());
}
} catch (Exception e) {
@@ -253,6 +255,16 @@ public class NodeValueFetcher {
}
}
+ private static Object getSysProp(Object systemProperties, String key) {
+ if (systemProperties instanceof NamedList<?> namedList) {
+ return namedList.get(key);
+ }
+ if (systemProperties instanceof Map<?, ?> map) {
+ return map.get(key);
+ }
+ return null;
+ }
+
/**
* Retrieve values that match metrics. Metrics names are structured like
below:
*