This is an automated email from the ASF dual-hosted git repository.
janhoy 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 a91522dc4d7 SOLR-18516: Port PostTool and PostLogsTool to picocli
(#5042)
a91522dc4d7 is described below
commit a91522dc4d72c5363b0de9e961a854f322b89a69
Author: Serhiy Bzhezytskyy <[email protected]>
AuthorDate: Thu Oct 8 19:02:20 2026 +0300
SOLR-18516: Port PostTool and PostLogsTool to picocli (#5042)
Co-authored-by: Jan Høydahl <[email protected]>
---
.../SOLR-17697-picocli-experimental-cli.yml | 2 +
.../src/java/org/apache/solr/cli/PostLogsTool.java | 63 ++++++-
.../src/java/org/apache/solr/cli/PostTool.java | 201 ++++++++++++++++++++-
.../core/src/java/org/apache/solr/cli/SolrCLI.java | 4 +-
.../org/apache/solr/cli/PostLogsToolCliTest.java | 83 +++++++++
.../apache/solr/cli/PostLogsToolPicocliTest.java | 39 ++++
.../org/apache/solr/cli/PostToolPicocliTest.java | 39 ++++
.../src/test/org/apache/solr/cli/PostToolTest.java | 13 +-
solr/packaging/build.gradle | 2 +
solr/packaging/test/test_postlogs.bats | 2 +-
.../modules/deployment-guide/deployment-nav.adoc | 2 +
.../modules/deployment-guide/pages/cli/index.adoc | 2 +
.../deployment-guide/pages/cli/solr-post.adoc | 130 +++++++++++++
.../deployment-guide/pages/cli/solr-postlogs.adoc | 84 +++++++++
.../modules/query-guide/pages/logs.adoc | 2 +-
15 files changed, 659 insertions(+), 9 deletions(-)
diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
index 84e285fbbb2..ffdd9051a75 100644
--- a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
+++ b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
@@ -23,3 +23,5 @@ links:
url: https://issues.apache.org/jira/browse/SOLR-18513
- name: SOLR-18514
url: https://issues.apache.org/jira/browse/SOLR-18514
+ - name: SOLR-18516
+ url: https://issues.apache.org/jira/browse/SOLR-18516
diff --git a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
index 2764ba4b9fb..5f02b57ece2 100644
--- a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
@@ -37,6 +37,7 @@ import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.common.SolrInputDocument;
@@ -45,8 +46,23 @@ import org.apache.solr.common.util.URLUtil;
import org.apache.solr.handler.component.ShardRequest;
/** A command line tool for indexing Solr logs in the out-of-the-box log
format. */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ name = "postlogs",
+ description = "Indexes Solr logs in the out-of-the-box log format.",
+ footerHeading = "%nExamples:%n",
+ footer = {
+ " # Index all logs found under a directory",
+ " bin/solr postlogs -c gettingstarted --rootdir /var/solr/logs
--solr-url"
+ + " http://localhost:8983"
+ })
public class PostLogsTool extends ToolBase {
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option COLLECTION_NAME_OPTION =
Option.builder("c")
.longOpt("name")
@@ -56,6 +72,11 @@ public class PostLogsTool extends ToolBase {
.desc("Name of the collection.")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option ROOT_DIR_OPTION =
Option.builder()
.longOpt("rootdir")
@@ -68,6 +89,33 @@ public class PostLogsTool extends ToolBase {
/** Parameters for the postlogs command, independent of the command line
parser. */
record PostLogsParams(String url, String rootDir, String credentials) {}
+ // --- picocli fields ---
+ // The connection group is mandatory (multiplicity "1"), mirroring the
commons-cli path's
+ // manual IllegalArgumentException when no connection target is given.
+
+ @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "1")
+ private ConnectionOptions connectionOptions;
+
+ @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions;
+
+ @picocli.CommandLine.Option(
+ names = {"-c", "--name"},
+ required = true,
+ paramLabel = "NAME",
+ description = "Name of the collection.")
+ private String nameOpt;
+
+ @picocli.CommandLine.Option(
+ names = "--rootdir",
+ required = true,
+ paramLabel = "DIRECTORY",
+ description = "All files found at or below the root directory will be
indexed.")
+ private String rootDirOpt;
+
+ public PostLogsTool() {
+ this(new DefaultToolRuntime());
+ }
+
public PostLogsTool(ToolRuntime runtime) {
super(runtime);
}
@@ -639,6 +687,19 @@ public class PostLogsTool extends ToolBase {
@Override
public int callTool() throws Exception {
- throw new UnsupportedOperationException("This tool does not yet support
PicoCli");
+ String url = resolveSolrUrl(credentialsOptions.credentials) + "/solr/" +
nameOpt;
+ PostLogsParams params = new PostLogsParams(url, rootDirOpt,
credentialsOptions.credentials);
+ runCommand(params);
+ return 0;
+ }
+
+ private String resolveSolrUrl(String credentials) throws Exception {
+ String solrUrlArg = connectionOptions.effectiveSolrUrl();
+ if (solrUrlArg != null) {
+ return CLIUtils.normalizeSolrUrl(solrUrlArg);
+ }
+ return CLIUtils.solrUrlFromConnection(
+
CloudSolrClient.CloudSolrClientConnection.parse(connectionOptions.effectiveZkHost()),
+ credentials);
}
}
diff --git a/solr/core/src/java/org/apache/solr/cli/PostTool.java
b/solr/core/src/java/org/apache/solr/cli/PostTool.java
index afd1c50467a..859cee11e21 100644
--- a/solr/core/src/java/org/apache/solr/cli/PostTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/PostTool.java
@@ -74,6 +74,7 @@ import org.apache.commons.io.output.NullOutputStream;
import org.apache.solr.client.api.util.SolrVersion;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.common.util.Utils;
import org.apache.solr.util.RTimer;
import org.w3c.dom.Document;
@@ -82,6 +83,23 @@ import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/** Supports post command in the bin/solr script. */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ name = "post",
+ description =
+ "The Post command sends one or more files, directories, URLs or
literal data to a"
+ + " collection for indexing.",
+ footerHeading = "%nExamples:%n",
+ footer = {
+ " # Index all JSON files into a collection",
+ " bin/solr post -c gettingstarted *.json",
+ "",
+ " # Index a tab-separated file, using the CSV handler's separator
param",
+ " bin/solr post -c gettingstarted --params \"separator=%%09\" --type
text/csv data.tsv",
+ "",
+ " # Crawl a website one layer deep and index the pages found",
+ " bin/solr post -c gettingstarted --mode web --recursive 1
https://solr.apache.org/"
+ })
public class PostTool extends ToolBase {
public static final String DEFAULT_FILE_TYPES =
@@ -96,6 +114,11 @@ public class PostTool extends ToolBase {
private static final int MAX_WEB_DEPTH = 10;
public static final String DEFAULT_CONTENT_TYPE = "application/json";
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option COLLECTION_NAME_OPTION =
Option.builder("c")
.longOpt("name")
@@ -105,18 +128,33 @@ public class PostTool extends ToolBase {
.desc("Name of the collection.")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option SKIP_COMMIT_OPTION =
Option.builder()
.longOpt("skip-commit")
.desc("Do not 'commit', and thus changes won't be visible till a
commit occurs.")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option OPTIMIZE_OPTION =
Option.builder("o")
.longOpt("optimize")
.desc("Issue an optimize at end of posting documents.")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option MODE_OPTION =
Option.builder()
.longOpt("mode")
@@ -126,6 +164,11 @@ public class PostTool extends ToolBase {
"Specifies how to run: 'files' crawls local directories, 'web'
crawls websites, 'args' processes positional arguments, and 'stdin' reads
standard input stream. Default: files.")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option RECURSIVE_OPTION =
Option.builder("r")
.longOpt("recursive")
@@ -135,6 +178,11 @@ public class PostTool extends ToolBase {
.desc("For web crawl, how deep to go. default: 1")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option DELAY_OPTION =
Option.builder("d")
.longOpt("delay")
@@ -145,6 +193,11 @@ public class PostTool extends ToolBase {
"If recursive then delay will be the wait time between posts.
default: 10 for web, 0 for files")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option TYPE_OPTION =
Option.builder("t")
.longOpt("type")
@@ -153,6 +206,11 @@ public class PostTool extends ToolBase {
.desc("Specify a specific mimetype to use, such as
application/json.")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option FILE_TYPES_OPTION =
Option.builder("ft")
.longOpt("filetypes")
@@ -161,6 +219,11 @@ public class PostTool extends ToolBase {
.desc("default: " + DEFAULT_FILE_TYPES)
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option PARAMS_OPTION =
Option.builder()
.longOpt("params")
@@ -169,6 +232,11 @@ public class PostTool extends ToolBase {
.desc("Values must be URL-encoded; these pass through to Solr update
request.")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option FORMAT_OPTION =
Option.builder()
.longOpt("format")
@@ -176,6 +244,11 @@ public class PostTool extends ToolBase {
"sends application/json content as Solr commands to /update
instead of /update/json/docs.")
.get();
+ /**
+ * @deprecated Only used by the commons-cli parser; the picocli path
declares this as an annotated
+ * field.
+ */
+ @Deprecated
private static final Option DRY_RUN_OPTION =
Option.builder()
.longOpt("dry-run")
@@ -212,6 +285,98 @@ public class PostTool extends ToolBase {
PostTool.PageFetcher pageFetcher = new PostTool.PageFetcher();
+ // --- picocli fields ---
+ // Named distinctly from the commons-cli-era instance fields above (type,
recursive, delay,
+ // fileTypes, params, commit, optimize, dryRun, args), which postDocuments()
still populates from
+ // whichever parser ran.
+
+ @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1")
+ private ConnectionOptions connectionOptions;
+
+ @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions;
+
+ @picocli.CommandLine.Option(
+ names = {"-c", "--name"},
+ required = true,
+ paramLabel = "name",
+ description = "Name of the collection.")
+ private String nameOpt;
+
+ @picocli.CommandLine.Option(
+ names = "--skip-commit",
+ description = "Do not 'commit', and thus changes won't be visible till a
commit occurs.")
+ private boolean skipCommitOpt;
+
+ @picocli.CommandLine.Option(
+ names = {"-o", "--optimize"},
+ description = "Issue an optimize at end of posting documents.")
+ private boolean optimizeOpt;
+
+ @picocli.CommandLine.Option(
+ names = "--mode",
+ defaultValue = DATA_MODE_FILES,
+ paramLabel = "mode",
+ description =
+ "Which mode the Post command is running in, 'files' crawls local
directory, 'web'"
+ + " crawls website, 'args' processes input args, and 'stdin'
reads a command from"
+ + " standard in. default: files.")
+ private String modeOpt;
+
+ @picocli.CommandLine.Option(
+ names = {"-r", "--recursive"},
+ defaultValue = "1",
+ paramLabel = "recursive",
+ description = "For web crawl, how deep to go. default: 1")
+ private int recursiveOpt;
+
+ @picocli.CommandLine.Option(
+ names = {"-d", "--delay"},
+ paramLabel = "delay",
+ description =
+ "If recursive then delay will be the wait time between posts.
default: 10 for web, 0"
+ + " for files")
+ private Integer delayOpt;
+
+ @picocli.CommandLine.Option(
+ names = {"-t", "--type"},
+ paramLabel = "content-type",
+ description = "Specify a specific mimetype to use, such as
application/json.")
+ private String typeOpt;
+
+ @picocli.CommandLine.Option(
+ names = {"-ft", "--filetypes"},
+ defaultValue = DEFAULT_FILE_TYPES,
+ paramLabel = "<type>[,<type>,...]",
+ description = "default: " + DEFAULT_FILE_TYPES)
+ private String fileTypesOpt;
+
+ @picocli.CommandLine.Option(
+ names = "--params",
+ defaultValue = "",
+ paramLabel = "<key>=<value>[&<key>=<value>...]",
+ description = "Values must be URL-encoded; these pass through to Solr
update request.")
+ private String paramsOpt;
+
+ @picocli.CommandLine.Option(
+ names = "--format",
+ description =
+ "sends application/json content as Solr commands to /update instead
of"
+ + " /update/json/docs.")
+ private boolean formatOpt;
+
+ @picocli.CommandLine.Option(
+ names = "--dry-run",
+ description =
+ "Performs a dry run of the posting process without actually sending
documents to"
+ + " Solr. Only works with files mode.")
+ private boolean dryRunOpt;
+
+ @picocli.CommandLine.Parameters(
+ arity = "0..*",
+ paramLabel = "FILE",
+ description = "Files, directories, urls or literal data to post,
depending on --mode.")
+ private String[] postArgs = new String[0];
+
static {
DATA_MODES.add(DATA_MODE_FILES);
DATA_MODES.add(DATA_MODE_ARGS);
@@ -280,6 +445,10 @@ public class PostTool extends ToolBase {
CrawlOptions crawl,
UpdateOptions update) {}
+ public PostTool() {
+ this(new DefaultToolRuntime());
+ }
+
public PostTool(ToolRuntime runtime) {
super(runtime);
}
@@ -1401,7 +1570,37 @@ public class PostTool extends ToolBase {
@Override
public int callTool() throws Exception {
- throw new UnsupportedOperationException("This tool does not yet support
PicoCli");
+ String resolvedSolrUrl = resolveSolrUrl(credentialsOptions.credentials);
+ URI updateUrl = new URI(resolvedSolrUrl + "/solr/" + nameOpt + "/update");
+ int defaultDelay = modeOpt.equals(DATA_MODE_WEB) ? DEFAULT_WEB_DELAY : 0;
+
+ PostToolParams postParams =
+ new PostToolParams(
+ updateUrl,
+ modeOpt,
+ dryRunOpt,
+ credentialsOptions.credentials,
+ postArgs,
+ new ContentOptions(typeOpt, formatOpt ? FORMAT_SOLR : "",
paramsOpt),
+ new CrawlOptions(
+ fileTypesOpt, delayOpt != null ? delayOpt : defaultDelay,
recursiveOpt),
+ new UpdateOptions(!skipCommitOpt, optimizeOpt));
+ postDocuments(postParams);
+ return 0;
+ }
+
+ /** As under commons-cli, a missing connection option falls back to the
default URL silently. */
+ private String resolveSolrUrl(String credentials) throws Exception {
+ String solrUrlArg = (connectionOptions != null) ?
connectionOptions.effectiveSolrUrl() : null;
+ if (solrUrlArg != null) {
+ return CLIUtils.normalizeSolrUrl(solrUrlArg);
+ }
+ String zkHostArg = (connectionOptions != null) ?
connectionOptions.effectiveZkHost() : null;
+ if (zkHostArg != null) {
+ return CLIUtils.solrUrlFromConnection(
+ CloudSolrClient.CloudSolrClientConnection.parse(zkHostArg),
credentials);
+ }
+ return CLIUtils.getDefaultSolrUrl();
}
/** Utility class to hold the result form a page fetch */
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 57b22e79d3e..d8b8e219613 100755
--- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
@@ -86,7 +86,9 @@ import org.slf4j.LoggerFactory;
ClusterTool.class,
ConfigTool.class,
ExportTool.class,
- HealthcheckTool.class
+ HealthcheckTool.class,
+ PostTool.class,
+ PostLogsTool.class
})
public class SolrCLI implements CLIO, java.util.concurrent.Callable<Integer> {
diff --git a/solr/core/src/test/org/apache/solr/cli/PostLogsToolCliTest.java
b/solr/core/src/test/org/apache/solr/cli/PostLogsToolCliTest.java
new file mode 100644
index 00000000000..43659498398
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/PostLogsToolCliTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Locale;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.SolrQuery;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class PostLogsToolCliTest extends SolrCloudTestCase {
+ private static final String COLLECTION = "postLogsToolColl";
+
+ /** Runs the tool. Overridden by the picocli variant of this test. */
+ protected int runTool(String[] args, Class<? extends ToolBase> clazz) throws
Exception {
+ return CLITestHelper.runTool(args, clazz);
+ }
+
+ @BeforeClass
+ public static void setupCluster() throws Exception {
+ configureCluster(1).addConfig("conf",
configset("cloud-minimal")).configure();
+ CollectionAdminRequest.createCollection(COLLECTION, "conf", 1, 1)
+ .process(cluster.getSolrClient());
+ cluster.waitForActiveCollection(COLLECTION, 1, 1);
+ }
+
+ private String solrUrl() {
+ return cluster.getJettySolrRunner(0).getBaseUrl().toString();
+ }
+
+ private Path logDirWithTwoQueries() throws Exception {
+ Path dir = createTempDir();
+ String record =
+ "2019-12-09 15:05:%02d.931 INFO (qtp2103763750-21) [c:logs4 s:shard1
r:core_node2 x:logs4_shard1_replica_n1] o.a.s.c.S.Request
[logs4_shard1_replica_n1] path=/select params={q=*:*&wt=javabin} hits=1
status=0 QTime=8\n";
+ Files.writeString(
+ dir.resolve("solr.log"),
+ String.format(Locale.ROOT, record, 11) + String.format(Locale.ROOT,
record, 12),
+ StandardCharsets.UTF_8);
+ return dir;
+ }
+
+ @Test
+ public void testPostsLogRecords() throws Exception {
+ String[] args = {
+ "postlogs",
+ "-c",
+ COLLECTION,
+ "--solr-url",
+ solrUrl(),
+ "--rootdir",
+ logDirWithTwoQueries().toString()
+ };
+ assertEquals(0, runTool(args, PostLogsTool.class));
+
+ long found =
+ cluster.getSolrClient().query(COLLECTION, new
SolrQuery("*:*")).getResults().getNumFound();
+ assertEquals(2, found);
+ }
+
+ @Test
+ public void testFailsWithoutAConnectionTarget() throws Exception {
+ String[] args = {"postlogs", "-c", COLLECTION, "--rootdir",
logDirWithTwoQueries().toString()};
+ assertNotEquals(0, runTool(args, PostLogsTool.class));
+ }
+}
diff --git
a/solr/core/src/test/org/apache/solr/cli/PostLogsToolPicocliTest.java
b/solr/core/src/test/org/apache/solr/cli/PostLogsToolPicocliTest.java
new file mode 100644
index 00000000000..39aa9da7c7e
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/PostLogsToolPicocliTest.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 PostLogsToolCliTest} tests through the picocli invocation
path.
+ *
+ * <p>All {@code @Test} methods are inherited; only the invocation strategy is
overridden.
+ */
+public class PostLogsToolPicocliTest extends PostLogsToolCliTest {
+
+ @Override
+ protected int runTool(String[] args, Class<? extends ToolBase> 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/PostToolPicocliTest.java
b/solr/core/src/test/org/apache/solr/cli/PostToolPicocliTest.java
new file mode 100644
index 00000000000..1a0de20e2c7
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/PostToolPicocliTest.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 PostToolTest} tests through the picocli invocation path.
+ *
+ * <p>All {@code @Test} methods are inherited; only the invocation strategy is
overridden.
+ */
+public class PostToolPicocliTest extends PostToolTest {
+
+ @Override
+ protected int runTool(String[] args, Class<? extends ToolBase> 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/PostToolTest.java
b/solr/core/src/test/org/apache/solr/cli/PostToolTest.java
index 5390fbeafcf..d3c7c0641a3 100644
--- a/solr/core/src/test/org/apache/solr/cli/PostToolTest.java
+++ b/solr/core/src/test/org/apache/solr/cli/PostToolTest.java
@@ -55,6 +55,11 @@ import org.junit.Test;
@SolrTestCaseJ4.SuppressSSL
public class PostToolTest extends SolrCloudTestCase {
+ /** Runs the tool. Overridden by the picocli variant of this test. */
+ protected int runTool(String[] args, Class<? extends ToolBase> clazz) throws
Exception {
+ return CLITestHelper.runTool(args, clazz);
+ }
+
@BeforeClass
public static void setupClusterWithSecurityEnabled() throws Exception {
configureCluster(2)
@@ -92,7 +97,7 @@ public class PostToolTest extends SolrCloudTestCase {
SecurityJson.USER_PASS,
jsonDoc.toString(),
};
- assertEquals(0, CLITestHelper.runTool(args, PostTool.class));
+ assertEquals(0, runTool(args, PostTool.class));
int numFound = 0;
int expectedDocCount = 1;
@@ -130,7 +135,7 @@ public class PostToolTest extends SolrCloudTestCase {
String[] args = {
"post", "-c", collection, "--credentials", SecurityJson.USER_PASS,
jsonDoc.toString(),
};
- assertEquals(0, CLITestHelper.runTool(args, PostTool.class));
+ assertEquals(0, runTool(args, PostTool.class));
int numFound = 0;
int expectedDocCount = 1;
@@ -171,12 +176,12 @@ public class PostToolTest extends SolrCloudTestCase {
"--credentials",
SecurityJson.USER_PASS,
"--params",
- "\"separator=%09&header=false&fieldnames=id,title_s\"",
+ "separator=%09&header=false&fieldnames=id,title_s",
"--type",
"text/csv",
tsvDoc.toString(),
};
- assertEquals(0, CLITestHelper.runTool(args, PostTool.class));
+ assertEquals(0, runTool(args, PostTool.class));
int numFound = 0;
int expectedDocCount = 1;
diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle
index 50efa69ddbf..dedb2783018 100644
--- a/solr/packaging/build.gradle
+++ b/solr/packaging/build.gradle
@@ -367,6 +367,8 @@ task integrationTestsPicocli(type: BatsTask) {
'test/test_modules.bats',
'test/test_security_manager.bats',
'test/test_healthcheck.bats',
+ 'test/test_postlogs.bats',
+ 'test/test_extraction.bats',
]
}
diff --git a/solr/packaging/test/test_postlogs.bats
b/solr/packaging/test/test_postlogs.bats
index 76893566d43..b54be528238 100644
--- a/solr/packaging/test/test_postlogs.bats
+++ b/solr/packaging/test/test_postlogs.bats
@@ -42,7 +42,7 @@ teardown() {
run solr create -c COLL_NAME
assert_output --partial "Created collection 'COLL_NAME'"
- run solr postlogs --solr-url http://localhost:${SOLR_PORT} -c COLL_NAME
-rootdir ${SOLR_LOGS_DIR}/solr.log
+ run solr postlogs --solr-url http://localhost:${SOLR_PORT} -c COLL_NAME
--rootdir ${SOLR_LOGS_DIR}/solr.log
assert_output --partial 'Sending last batch'
assert_output --partial 'Committed'
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 8336adf0a11..25397dcfe32 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
@@ -29,6 +29,8 @@
*** xref:cli/solr-delete.adoc[delete]
*** xref:cli/solr-export.adoc[export]
*** xref:cli/solr-healthcheck.adoc[healthcheck]
+*** xref:cli/solr-post.adoc[post]
+*** xref:cli/solr-postlogs.adoc[postlogs]
*** xref:cli/solr-start.adoc[start]
*** xref:cli/solr-status.adoc[status]
*** xref:cli/solr-stop.adoc[stop]
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 4bf311d3315..c542dbb975b 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
@@ -68,6 +68,8 @@ Any command not listed here is unavailable when the picocli
implementation is en
|xref:cli/solr-delete.adoc[bin/solr delete]
|xref:cli/solr-export.adoc[bin/solr export]
|xref:cli/solr-healthcheck.adoc[bin/solr healthcheck]
+|xref:cli/solr-post.adoc[bin/solr post]
+|xref:cli/solr-postlogs.adoc[bin/solr postlogs]
|xref:cli/solr-start.adoc[bin/solr start]
|xref:cli/solr-status.adoc[bin/solr status]
|xref:cli/solr-stop.adoc[bin/solr stop]
diff --git
a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-post.adoc
b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-post.adoc
new file mode 100644
index 00000000000..df98377f08f
--- /dev/null
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-post.adoc
@@ -0,0 +1,130 @@
+// 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 post
+: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 post [-hov] [--dry-run] [--format] [--skip-commit] -c=name [-d=delay]
+ [-ft=<type>[,<type>,...]] [--mode=mode] [--params=<key>=<value>
+ [&<key>=<value>...]] [-r=recursive] [-t=content-type]
+ [-u=<credentials>] [-s=<solrConnection> | --solr-url=<solrUrl> |
+ -z=<zkHost>] [FILE...]
+....
+
+// end::picocli-generated-man-section-synopsis[]
+
+// tag::picocli-generated-man-section-description[]
+== Description
+
+The Post command sends one or more files, directories, URLs or literal data to
a collection for indexing.
+
+// end::picocli-generated-man-section-description[]
+
+// tag::picocli-generated-man-section-options[]
+== Options
+
+*-c*, *--name*=_name_::
+ *(required)* Name of the collection.
+
+*-d*, *--delay*=_delay_::
+ If recursive then delay will be the wait time between posts. default: 10
for web, 0 for files
+
+*--dry-run*::
+ Performs a dry run of the posting process without actually sending documents
to Solr. Only works with files mode.
+
+*--format*::
+ sends application/json content as Solr commands to /update instead of
/update/json/docs.
+
+*-ft, --filetypes*=_<type>[,<type>,...]_::
+ default:
xml,json,jsonl,csv,pdf,doc,docx,ppt,pptx,xls,xlsx,odt,odp,ods,ott,otp,ots,rtf,htm,html,txt,log
+
+*-h*, *--help*::
+ Print this help message and exit.
+
+*--mode*=_mode_::
+ Which mode the Post command is running in, 'files' crawls local directory,
'web' crawls website, 'args' processes input args, and 'stdin' reads a command
from standard in. default: files.
+
+*-o*, *--optimize*::
+ Issue an optimize at end of posting documents.
+
+*--params*=_<key>=<value>[&<key>=<value>...]_::
+ Values must be URL-encoded; these pass through to Solr update request.
+
+*-r*, *--recursive*=_recursive_::
+ For web crawl, how deep to go. default: 1
+
+*-s*, *--solr-connection*=_<solrConnection>_::
+ Zookeeper or HTTP(s) connection string; unnecessary if SOLR_CONNECTION is
defined in solr.in.sh; otherwise, defaults to localhost:9983.
+
+*--skip-commit*::
+ Do not 'commit', and thus changes won't be visible till a commit occurs.
+
+*--solr-url*=_<solrUrl>_::
+ Base Solr URL, which can be used to determine the zk-host if that's not
known.
+
+*-t*, *--type*=_content-type_::
+ Specify a specific mimetype to use, such as application/json.
+
+*-u*, *--credentials*=_<credentials>_::
+ Credentials in the format username:password. Example: --credentials
solr:SolrRocks
+
+*-v*, *--verbose*::
+ Enable verbose mode.
+
+*-z*, *--zk-host*=_<zkHost>_::
+ Zookeeper connection string; unnecessary if ZK_HOST is defined in
solr.in.sh; otherwise, defaults to localhost:9983.
+
+// end::picocli-generated-man-section-options[]
+
+// tag::picocli-generated-man-section-arguments[]
+== Arguments
+
+[_FILE_...]::
+ Files, directories, urls or literal data to post, depending on --mode.
+
+// end::picocli-generated-man-section-arguments[]
+
+// tag::picocli-generated-man-section-footer[]
+== Examples
+[%hardbreaks]
+ # Index all JSON files into a collection
+ bin/solr post -c gettingstarted *.json
+
+[%hardbreaks]
+ # Index a tab-separated file, using the CSV handler's separator param
+ bin/solr post -c gettingstarted --params "separator=%09" --type text/csv
data.tsv
+
+[%hardbreaks]
+ # Crawl a website one layer deep and index the pages found
+ bin/solr post -c gettingstarted --mode web --recursive 1
https://solr.apache.org/
+
+// end::picocli-generated-man-section-footer[]
diff --git
a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-postlogs.adoc
b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-postlogs.adoc
new file mode 100644
index 00000000000..61c4a1a94cd
--- /dev/null
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-postlogs.adoc
@@ -0,0 +1,84 @@
+// 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 postlogs
+: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 postlogs [-hv] -c=NAME --rootdir=DIRECTORY [-u=<credentials>]
+ (-s=<solrConnection> | --solr-url=<solrUrl> | -z=<zkHost>)
+....
+
+// end::picocli-generated-man-section-synopsis[]
+
+// tag::picocli-generated-man-section-description[]
+== Description
+
+Indexes Solr logs in the out-of-the-box log format.
+
+// end::picocli-generated-man-section-description[]
+
+// tag::picocli-generated-man-section-options[]
+== Options
+
+*-c*, *--name*=_NAME_::
+ *(required)* Name of the collection.
+
+*-h*, *--help*::
+ Print this help message and exit.
+
+*--rootdir*=_DIRECTORY_::
+ *(required)* All files found at or below the root directory will be indexed.
+
+*-s*, *--solr-connection*=_<solrConnection>_::
+ Zookeeper or HTTP(s) connection string; unnecessary if SOLR_CONNECTION is
defined in solr.in.sh; otherwise, defaults to localhost:9983.
+
+*--solr-url*=_<solrUrl>_::
+ Base Solr URL, which can be used to determine the zk-host if that's not
known.
+
+*-u*, *--credentials*=_<credentials>_::
+ Credentials in the format username:password. Example: --credentials
solr:SolrRocks
+
+*-v*, *--verbose*::
+ Enable verbose mode.
+
+*-z*, *--zk-host*=_<zkHost>_::
+ Zookeeper connection string; unnecessary if ZK_HOST is defined in
solr.in.sh; otherwise, defaults to localhost:9983.
+
+// end::picocli-generated-man-section-options[]
+
+// tag::picocli-generated-man-section-footer[]
+== Examples
+[%hardbreaks]
+ # Index all logs found under a directory
+ bin/solr postlogs -c gettingstarted --rootdir /var/solr/logs --solr-url
http://localhost:8983
+
+// end::picocli-generated-man-section-footer[]
diff --git a/solr/solr-ref-guide/modules/query-guide/pages/logs.adoc
b/solr/solr-ref-guide/modules/query-guide/pages/logs.adoc
index 6983720f754..fe8f11a3aa0 100644
--- a/solr/solr-ref-guide/modules/query-guide/pages/logs.adoc
+++ b/solr/solr-ref-guide/modules/query-guide/pages/logs.adoc
@@ -46,7 +46,7 @@ The `postlogs` command reads in Solr's log format and indexes
it in a Solr colle
Address of the collection, example
http://localhost:8983/solr/\{collectionName}/.
+
-`-rootdir <directory>`::
+`--rootdir <directory>`::
+
[%autowidth,frame=none]
|===