From f73ea38893f9bff4647b5035b5a7e862913473b5 Mon Sep 17 00:00:00 2001 From: prithvi Date: Fri, 21 Aug 2026 04:31:08 +0530 Subject: [PATCH 1/2] Migrate node properties API to JAX-RS --- ...SOLR-16458-migrate-node-properties-api.yml | 8 ++ .../api/endpoint/NodePropertiesApi.java | 38 +++++++ .../api/model/NodePropertiesResponse.java | 31 ++++++ .../admin/PropertiesRequestHandler.java | 42 ++++---- .../handler/admin/api/GetNodeProperties.java | 70 ++++++++++++ .../handler/admin/api/NodePropertiesAPI.java | 47 -------- .../solr/handler/admin/InfoHandlerTest.java | 2 +- .../admin/PropertiesRequestHandlerTest.java | 27 ++++- .../admin/api/GetNodePropertiesTest.java | 100 ++++++++++++++++++ .../admin/api/V2NodeAPIMappingTest.java | 24 ----- 10 files changed, 293 insertions(+), 96 deletions(-) create mode 100644 changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml create mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/NodePropertiesApi.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/NodePropertiesResponse.java create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/GetNodeProperties.java delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/NodePropertiesAPI.java create mode 100644 solr/core/src/test/org/apache/solr/handler/admin/api/GetNodePropertiesTest.java 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 000000000000..f5524a2d596a --- /dev/null +++ b/changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml @@ -0,0 +1,8 @@ +title: "SolrJ now offers a SolrRequest class allowing users to fetch node system properties: NodeApi.GetNodeProperties" +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 000000000000..f8d55acb524f --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/NodePropertiesApi.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.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.QueryParam; +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( + @Parameter(description = "Optional name of a single system property to return.") + @QueryParam("name") + String name); +} 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 000000000000..f2c4065767b4 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/NodePropertiesResponse.java @@ -0,0 +1,31 @@ +/* + * 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 the {@code GET /api/node/properties} endpoint. */ +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 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 8658adf3c528..37f9a786c36e 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 @@ -16,24 +16,27 @@ */ package org.apache.solr.handler.admin; +import static org.apache.solr.client.api.model.NodePropertiesResponse.SYSTEM_PROPERTIES; 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 java.util.Map; import org.apache.solr.api.Api; +import org.apache.solr.api.JerseyResource; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; 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.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 { @@ -50,22 +53,14 @@ public PropertiesRequestHandler(CoreContainer cc) { } @Override - public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { - NamedList 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); + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { rsp.setHttpCaching(false); + String name = req.getParams().get(NAME); + Map props = + new GetNodeProperties(getCoreContainer(req)).collectProperties(name); + NamedList values = new SimpleOrderedMap<>(); + props.forEach(values::add); + rsp.add(SYSTEM_PROPERTIES, values); } //////////////////////// SolrInfoMBeans methods ////////////////////// @@ -82,7 +77,12 @@ public Category getCategory() { @Override public Collection getApis() { - return AnnotatedApi.getApis(new NodePropertiesAPI(this)); + return List.of(); + } + + @Override + public Collection> 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 000000000000..3048b893e092 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetNodeProperties.java @@ -0,0 +1,70 @@ +/* + * 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.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. + * + *

This API (GET /v2/node/properties) is analogous to the v1 /admin/info/properties. + * + *

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(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 collectProperties(String name) { + final NodeConfig nodeConfig = coreContainer.getNodeConfig(); + final Map 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 d9cf81f8a8b1..000000000000 --- 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. - * - *

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/InfoHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/InfoHandlerTest.java index 3e0100b14fbf..90f95cb86ddb 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/InfoHandlerTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/InfoHandlerTest.java @@ -111,7 +111,7 @@ public static class CountPropertiesRequestHandler extends PropertiesRequestHandl private int requestCount = 0; @Override - public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { ++requestCount; super.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 f64b96ae3114..2c7854e75986 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 @@ -21,6 +21,7 @@ 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; @@ -54,12 +55,32 @@ public void testRedaction() throws Exception { } } - @SuppressWarnings({"unchecked"}) + @Test + public void testSingleProperty() throws Exception { + System.setProperty("GetNodeProperties.v1.visible", "hello"); + try { + NamedList properties = readProperties("GetNodeProperties.v1.visible"); + assertEquals(1, properties.size()); + assertEquals("hello", properties.get("GetNodeProperties.v1.visible")); + } finally { + System.clearProperty("GetNodeProperties.v1.visible"); + } + } + private NamedList readProperties() throws Exception { - SolrClient client = new EmbeddedSolrServer(h.getCore()); + return readProperties(null); + } + @SuppressWarnings({"unchecked"}) + private NamedList readProperties(String name) throws Exception { + SolrClient client = new EmbeddedSolrServer(h.getCore()); + ModifiableSolrParams params = new ModifiableSolrParams(); + if (name != null) { + params.set("name", name); + } NamedList properties = - client.request(new GenericSolrRequest(SolrRequest.METHOD.GET, "/admin/info/properties")); + client.request( + new GenericSolrRequest(SolrRequest.METHOD.GET, "/admin/info/properties", params)); return (NamedList) 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 000000000000..55e0799a29a8 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetNodePropertiesTest.java @@ -0,0 +1,100 @@ +/* + * 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 org.apache.solr.SolrTestCase; +import org.apache.solr.client.api.model.NodePropertiesResponse; +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} via the generated SolrJ client class {@code + * NodeApi.GetNodeProperties}. + */ +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.GetNodeProperties(); + req.setName("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); + + assertTrue("expected more than one system property", rsp.systemProperties.size() > 1); + 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)); + } + + private NodePropertiesResponse fetchProperties(String name) throws Exception { + var req = new NodeApi.GetNodeProperties(); + if (name != null) { + req.setName(name); + } + NodePropertiesResponse rsp = req.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 6b3c63de45b4..ac2b660bc88e 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.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 queryRequestCaptor; private CoreAdminHandler mockCoresHandler; private InfoHandler infoHandler; - private LoggingHandler mockLoggingHandler; - private PropertiesRequestHandler mockPropertiesHandler; private ThreadDumpHandler mockThreadDumpHandler; @BeforeClass @@ -65,13 +61,9 @@ public static void ensureWorkingMockito() { 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 void testRejoinLeaderElectionApiAllProperties() throws Exception { 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 @@ private SolrParams captureConvertedCoreV1Params(String path, String method, Stri 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 @@ private static void registerAllNodeApis( 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())); } } From 6cad9eae9642119a3251fd0a8ffce8a67cea4764 Mon Sep 17 00:00:00 2001 From: prithvi Date: Fri, 21 Aug 2026 22:21:03 +0530 Subject: [PATCH 2/2] review chnages --- .../SOLR-16458-migrate-node-properties-api.yml | 2 +- .../client/api/endpoint/NodePropertiesApi.java | 16 +++++++++++----- .../client/api/model/NodePropertiesResponse.java | 5 ++++- .../handler/admin/api/GetNodeProperties.java | 16 ++++++++++++++-- .../handler/admin/api/GetNodePropertiesTest.java | 16 +++++++--------- .../pages/implicit-requesthandlers.adoc | 9 ++++++++- .../deployment-guide/pages/jvm-settings.adoc | 4 ++++ 7 files changed, 49 insertions(+), 19 deletions(-) diff --git a/changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml b/changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml index f5524a2d596a..2f4a23beaa48 100644 --- a/changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml +++ b/changelog/unreleased/SOLR-16458-migrate-node-properties-api.yml @@ -1,4 +1,4 @@ -title: "SolrJ now offers a SolrRequest class allowing users to fetch node system properties: NodeApi.GetNodeProperties" +title: "v2 GET /api/node/properties is now a JAX-RS API; a single property is fetched at /api/node/properties/{propertyName} (SolrJ: NodeApi.GetNodeProperties / GetNodeProperty)" type: added authors: - name: Prithvi S 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 index f8d55acb524f..7c986e5ad828 100644 --- 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 @@ -20,7 +20,7 @@ import io.swagger.v3.oas.annotations.Parameter; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; -import jakarta.ws.rs.QueryParam; +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. */ @@ -31,8 +31,14 @@ public interface NodePropertiesApi { @Operation( summary = "List system properties for the target Solr node.", tags = {"node"}) - NodePropertiesResponse getNodeProperties( - @Parameter(description = "Optional name of a single system property to return.") - @QueryParam("name") - String name); + 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 index f2c4065767b4..81ca0331fdde 100644 --- 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 @@ -20,7 +20,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import java.util.Map; -/** Response body for the {@code GET /api/node/properties} endpoint. */ +/** + * 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"; 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 index 3048b893e092..5296b1cd7760 100644 --- 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 @@ -31,7 +31,9 @@ /** * V2 API for listing system properties on the receiving node. * - *

This API (GET /v2/node/properties) is analogous to the v1 /admin/info/properties. + *

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. * *

The v1 {@link org.apache.solr.handler.admin.PropertiesRequestHandler} delegates to this class. */ @@ -46,7 +48,17 @@ public GetNodeProperties(CoreContainer coreContainer) { @Override @PermissionName(PermissionNameProvider.Name.CONFIG_READ_PERM) - public NodePropertiesResponse getNodeProperties(String name) { + public NodePropertiesResponse getNodeProperties() { + return buildResponse(null); + } + + @Override + @PermissionName(PermissionNameProvider.Name.CONFIG_READ_PERM) + public NodePropertiesResponse getNodeProperty(String propertyName) { + return buildResponse(propertyName); + } + + private NodePropertiesResponse buildResponse(String name) { final NodePropertiesResponse response = instantiateJerseyResponse(NodePropertiesResponse.class); response.systemProperties = collectProperties(name); return response; 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 index 55e0799a29a8..79745cf66353 100644 --- 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 @@ -27,8 +27,8 @@ import org.junit.Test; /** - * HTTP tests for {@code GET /api/node/properties} via the generated SolrJ client class {@code - * NodeApi.GetNodeProperties}. + * 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 { @@ -51,8 +51,7 @@ public void clearTestProperties() { @Test public void testNamedProperty() throws Exception { - var req = new NodeApi.GetNodeProperties(); - req.setName("java.version"); + var req = new NodeApi.GetNodeProperty("java.version"); var rsp = req.process(solrTestRule.getAdminClient()); assertNotNull(rsp); @@ -87,11 +86,10 @@ public void testRedactsHiddenProperties() throws Exception { } private NodePropertiesResponse fetchProperties(String name) throws Exception { - var req = new NodeApi.GetNodeProperties(); - if (name != null) { - req.setName(name); - } - NodePropertiesResponse rsp = req.process(solrTestRule.getAdminClient()); + 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); 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 4380337752c9..005742a7cec8 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 a40cdc4073ef..a426496caa71 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 @@ -78,6 +78,10 @@ Many of the system environment variables include Java settings, and these can be The Java Properties screen, however, provides easy access to all the properties of the JVM running Solr, including the classpaths, file encodings, JVM memory settings, operating system, and more. +The same information is available from the Node Properties API: `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. + In the Admin UI, it is available in the left-hand menu *Java Properties*. .Java Properties Screen