From a2960c2a71ae9ad74cd42493d2e439bfcd747086 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Tue, 6 Oct 2026 18:02:49 +0300 Subject: [PATCH 1/3] SOLR-18509: Port ApiTool to picocli Adds the picocli code path next to the commons-cli one, with a test twin, the generated reference page and test_modules.bats and test_security_manager.bats in the picocli suite. --- .../unreleased/SOLR-18509-picocli-api.yml | 9 +++ .../src/java/org/apache/solr/cli/ApiTool.java | 33 ++++++++- .../src/java/org/apache/solr/cli/SolrCLI.java | 3 +- .../apache/solr/cli/ApiToolPicocliTest.java | 39 ++++++++++ .../test/org/apache/solr/cli/ApiToolTest.java | 19 +++++ solr/packaging/build.gradle | 2 + .../deployment-guide/deployment-nav.adoc | 1 + .../deployment-guide/pages/cli/index.adoc | 1 + .../deployment-guide/pages/cli/solr-api.adoc | 71 +++++++++++++++++++ 9 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 changelog/unreleased/SOLR-18509-picocli-api.yml create mode 100644 solr/core/src/test/org/apache/solr/cli/ApiToolPicocliTest.java create mode 100644 solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc diff --git a/changelog/unreleased/SOLR-18509-picocli-api.yml b/changelog/unreleased/SOLR-18509-picocli-api.yml new file mode 100644 index 000000000000..683247ad4a68 --- /dev/null +++ b/changelog/unreleased/SOLR-18509-picocli-api.yml @@ -0,0 +1,9 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc + +title: The `api` command is now available in the experimental picocli command line interface. +type: added +authors: + - name: Serhiy Bzhezytskyy +links: + - name: SOLR-18509 + url: https://issues.apache.org/jira/browse/SOLR-18509 diff --git a/solr/core/src/java/org/apache/solr/cli/ApiTool.java b/solr/core/src/java/org/apache/solr/cli/ApiTool.java index 3f174ed58b81..81a268714e3e 100644 --- a/solr/core/src/java/org/apache/solr/cli/ApiTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ApiTool.java @@ -31,8 +31,22 @@ * *

Used to send an arbitrary HTTP request to a Solr API endpoint. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "api", + description = "Used to send an arbitrary HTTP GET request to a Solr API endpoint.", + footerHeading = "%nExamples:%n", + footer = { + " # Send a GET request to a Solr API endpoint", + " bin/solr api -s http://localhost:8983/api/cluster" + }) public class ApiTool extends ToolBase { + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option SOLR_URL_OPTION = Option.builder("s") .longOpt("solr-url") @@ -45,6 +59,21 @@ public class ApiTool extends ToolBase { /** Parameters for the api command, independent of the command line parser. */ record ApiParams(String getUrl, String credentials) {} + // --- picocli fields --- + + @picocli.CommandLine.Option( + names = {"-s", "--solr-url"}, + required = true, + paramLabel = "URL", + description = "Send a GET request to a Solr API endpoint.") + private String solrUrlOpt; + + @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; + + public ApiTool() { + this(new DefaultToolRuntime()); + } + public ApiTool(ToolRuntime runtime) { super(runtime); } @@ -124,6 +153,8 @@ public static ModifiableSolrParams getSolrParamsFromUri(URI uri) { @Override public int callTool() throws Exception { - throw new UnsupportedOperationException("This tool does not yet support PicoCli"); + ApiParams params = new ApiParams(solrUrlOpt, credentialsOptions.credentials); + callApi(params); + return 0; } } diff --git a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java index a80d7f7883e8..bf0ed3dd7a08 100755 --- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java +++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java @@ -80,7 +80,8 @@ ZkTool.class, AuthTool.class, CreateTool.class, - DeleteTool.class + DeleteTool.class, + ApiTool.class }) public class SolrCLI implements CLIO, java.util.concurrent.Callable { diff --git a/solr/core/src/test/org/apache/solr/cli/ApiToolPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/ApiToolPicocliTest.java new file mode 100644 index 000000000000..ef8c7ab3ba6b --- /dev/null +++ b/solr/core/src/test/org/apache/solr/cli/ApiToolPicocliTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.cli; + +import java.util.Arrays; +import picocli.CommandLine; + +/** + * Runs all {@link ApiToolTest} tests through the picocli invocation path. + * + *

All {@code @Test} methods are inherited; only the invocation strategy is overridden. + */ +public class ApiToolPicocliTest extends ApiToolTest { + + @Override + protected int runTool(String[] args, Class clazz) throws Exception { + // args[0] is the tool name used by commons-cli dispatch; strip it for picocli. + String[] toolArgs = Arrays.copyOfRange(args, 1, args.length); + ToolRuntime runtime = new CLITestHelper.TestingRuntime(false); + ToolBase tool = clazz.getDeclaredConstructor(ToolRuntime.class).newInstance(runtime); + return new CommandLine(tool) + .setDefaultValueProvider(new CliDefaultValueProvider()) + .execute(toolArgs); + } +} diff --git a/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java b/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java index 6893e51dc227..c44c5258c27d 100644 --- a/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java +++ b/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java @@ -32,6 +32,11 @@ public class ApiToolTest extends SolrCloudTestCase { static String COLLECTION_NAME = "globalLoaderColl"; + /** Runs the tool. Overridden by the picocli variant of this test. */ + protected int runTool(String[] args, Class clazz) throws Exception { + return CLITestHelper.runTool(args, clazz); + } + @BeforeClass public static void setupCluster() throws Exception { configureCluster(1) @@ -40,6 +45,20 @@ public static void setupCluster() throws Exception { .configure(); } + @Test + public void testApiCommandSucceedsOnAnOkResponse() throws Exception { + String url = cluster.getJettySolrRunner(0).getBaseUrl() + "/admin/info/system"; + + assertEquals(0, runTool(new String[] {"api", "-s", url}, ApiTool.class)); + } + + @Test + public void testApiCommandFailsOnANon2xxResponse() throws Exception { + String url = cluster.getJettySolrRunner(0).getBaseUrl() + "/admin/no-such-handler"; + + assertEquals(1, runTool(new String[] {"api", "-s", url}, ApiTool.class)); + } + @Test public void testParsingGetUrl() throws URISyntaxException { diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle index 9672174b2a43..1eed009e9f75 100644 --- a/solr/packaging/build.gradle +++ b/solr/packaging/build.gradle @@ -363,6 +363,8 @@ task integrationTestsPicocli(type: BatsTask) { 'test/test_create.bats', 'test/test_delete_collection.bats', 'test/test_zk.bats', + 'test/test_modules.bats', + 'test/test_security_manager.bats', ] } diff --git a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc index 309b2bd959f6..c640e12d230d 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc @@ -20,6 +20,7 @@ * xref:solr-control-script-reference.adoc[] ** xref:cli/index.adoc[Experimental picocli CLI] // CLI-DOCS-START +*** xref:cli/solr-api.adoc[api] *** xref:cli/solr-auth.adoc[auth] *** xref:cli/solr-create.adoc[create] *** xref:cli/solr-delete.adoc[delete] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc index f00109598068..96a83451825a 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc @@ -59,6 +59,7 @@ Any command not listed here is unavailable when the picocli implementation is en |=== |Command +|xref:cli/solr-api.adoc[bin/solr api] |xref:cli/solr-auth.adoc[bin/solr auth] |xref:cli/solr-create.adoc[bin/solr create] |xref:cli/solr-delete.adoc[bin/solr delete] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc new file mode 100644 index 000000000000..ce9603c0096a --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc @@ -0,0 +1,71 @@ +// 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. +// +// DO NOT EDIT -- this page is auto-generated from picocli annotations. +// To update: modify the @Command/@Option annotations in the Java source, then run: +// ./gradlew :solr:solr-ref-guide:generateCliDocs + += bin/solr api +:page-toclevels: 2 + +[WARNING] +==== +This page documents the *experimental* picocli implementation of `bin/solr`, which is not enabled by default. +For what `bin/solr` does by default, see xref:solr-control-script-reference.adoc[]. +See xref:cli/index.adoc[] to enable this implementation and for its known limitations. +==== + +// tag::picocli-generated-man-section-synopsis[] +== Synopsis + +.... +bin/solr api [-hv] -s=URL [-u=] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Used to send an arbitrary HTTP GET request to a Solr API endpoint. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*-h*, *--help*:: + Print this help message and exit. + +*-s*, *--solr-url*=_URL_:: + *(required)* Send a GET request to a Solr API endpoint. + +*-u*, *--credentials*=__:: + Credentials in the format username:password. Example: --credentials solr:SolrRocks + +*-v*, *--verbose*:: + Enable verbose mode. + +// end::picocli-generated-man-section-options[] + +// tag::picocli-generated-man-section-footer[] +== Examples +[%hardbreaks] + # Send a GET request to a Solr API endpoint + bin/solr api -s http://localhost:8983/api/cluster + +// end::picocli-generated-man-section-footer[] From 2c46bd1aabb71069b90d01c95567336272177c5c Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 15:07:32 +0300 Subject: [PATCH 2/3] SOLR-18509: Describe the api --solr-url value, not the command The option text repeated what the command does; the generated page now says what the value is, with an example. The footer example also pointed at GET /api/cluster, which SOLR-18450 removed, so it uses /api/collections. --- solr/core/src/java/org/apache/solr/cli/ApiTool.java | 5 +++-- .../modules/deployment-guide/pages/cli/solr-api.adoc | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/cli/ApiTool.java b/solr/core/src/java/org/apache/solr/cli/ApiTool.java index 81a268714e3e..0f904185bdcb 100644 --- a/solr/core/src/java/org/apache/solr/cli/ApiTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ApiTool.java @@ -38,7 +38,7 @@ footerHeading = "%nExamples:%n", footer = { " # Send a GET request to a Solr API endpoint", - " bin/solr api -s http://localhost:8983/api/cluster" + " bin/solr api -s http://localhost:8983/api/collections" }) public class ApiTool extends ToolBase { @@ -65,7 +65,8 @@ record ApiParams(String getUrl, String credentials) {} names = {"-s", "--solr-url"}, required = true, paramLabel = "URL", - description = "Send a GET request to a Solr API endpoint.") + description = + "Full URL of the Solr API endpoint to GET, e.g. http://localhost:8983/api/collections") private String solrUrlOpt; @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc index ce9603c0096a..cccb38dc9e95 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc @@ -52,7 +52,7 @@ Used to send an arbitrary HTTP GET request to a Solr API endpoint. Print this help message and exit. *-s*, *--solr-url*=_URL_:: - *(required)* Send a GET request to a Solr API endpoint. + *(required)* Full URL of the Solr API endpoint to GET, e.g. http://localhost:8983/api/collections *-u*, *--credentials*=__:: Credentials in the format username:password. Example: --credentials solr:SolrRocks @@ -66,6 +66,6 @@ Used to send an arbitrary HTTP GET request to a Solr API endpoint. == Examples [%hardbreaks] # Send a GET request to a Solr API endpoint - bin/solr api -s http://localhost:8983/api/cluster + bin/solr api -s http://localhost:8983/api/collections // end::picocli-generated-man-section-footer[] From cf1649319b51c637213fa68ff8783c0607bf8313 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 15:42:53 +0300 Subject: [PATCH 3/3] SOLR-18509: Add the change to the shared picocli changelog entry No picocli feature has been released yet, so a new command is a detail of the SOLR-17697 entry. Its author and JIRA are added there instead of a separate entry. --- .../unreleased/SOLR-17697-picocli-experimental-cli.yml | 3 +++ changelog/unreleased/SOLR-18509-picocli-api.yml | 9 --------- 2 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 changelog/unreleased/SOLR-18509-picocli-api.yml diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml index 85cdf1fd90d4..2d37be095da5 100644 --- a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml +++ b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml @@ -7,6 +7,9 @@ type: added authors: - name: Jan Høydahl url: https://home.apache.org/phonebook.html?uid=janhoy + - name: Serhiy Bzhezytskyy links: - name: SOLR-17697 url: https://issues.apache.org/jira/browse/SOLR-17697 + - name: SOLR-18509 + url: https://issues.apache.org/jira/browse/SOLR-18509 diff --git a/changelog/unreleased/SOLR-18509-picocli-api.yml b/changelog/unreleased/SOLR-18509-picocli-api.yml deleted file mode 100644 index 683247ad4a68..000000000000 --- a/changelog/unreleased/SOLR-18509-picocli-api.yml +++ /dev/null @@ -1,9 +0,0 @@ -# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc - -title: The `api` command is now available in the experimental picocli command line interface. -type: added -authors: - - name: Serhiy Bzhezytskyy -links: - - name: SOLR-18509 - url: https://issues.apache.org/jira/browse/SOLR-18509