Skip to content

Commit f9d962c

Browse files
dsmileyepughCopilot
authored
Refactor CLI StreamTool SolrClientCache usage (#4513)
Signed-off-by: Eric Pugh <epugh@opensourceconnections.com> Co-authored-by: Eric Pugh <epugh@opensourceconnections.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent b29e2b5 commit f9d962c

2 files changed

Lines changed: 71 additions & 65 deletions

File tree

solr/core/src/java/org/apache/solr/cli/CLIUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public static SolrClient getSolrClient(CommandLine cli) throws Exception {
180180
* is used, and warns those users. In the future we'll have urls ending with /api as well.
181181
*
182182
* @param solrUrl The user supplied url to Solr.
183-
* @return the solrUrl in the format that Solr expects to see internally.
183+
* @return a URL without any path, e.g. {@code http://localhost:8983}
184184
*/
185185
public static String normalizeSolrUrl(String solrUrl) {
186186
return normalizeSolrUrl(solrUrl, true);
@@ -192,7 +192,7 @@ public static String normalizeSolrUrl(String solrUrl) {
192192
*
193193
* @param solrUrl The user supplied url to Solr.
194194
* @param logUrlFormatWarning If a warning message should be logged about the url format
195-
* @return the solrUrl in the format that Solr expects to see internally.
195+
* @return a URL without any path, e.g. {@code http://localhost:8983}
196196
*/
197197
public static String normalizeSolrUrl(String solrUrl, boolean logUrlFormatWarning) {
198198
if (solrUrl != null) {

solr/core/src/java/org/apache/solr/cli/StreamTool.java

Lines changed: 69 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,22 @@
3838
import org.apache.commons.cli.CommandLine;
3939
import org.apache.commons.cli.Option;
4040
import org.apache.commons.cli.Options;
41-
import org.apache.solr.client.solrj.io.Lang;
4241
import org.apache.solr.client.solrj.io.SolrClientCache;
4342
import org.apache.solr.client.solrj.io.Tuple;
4443
import org.apache.solr.client.solrj.io.comp.StreamComparator;
4544
import org.apache.solr.client.solrj.io.stream.PushBackStream;
4645
import org.apache.solr.client.solrj.io.stream.SolrStream;
4746
import org.apache.solr.client.solrj.io.stream.StreamContext;
4847
import org.apache.solr.client.solrj.io.stream.TupleStream;
48+
import org.apache.solr.client.solrj.io.stream.expr.DefaultStreamFactory;
4949
import org.apache.solr.client.solrj.io.stream.expr.Explanation;
5050
import org.apache.solr.client.solrj.io.stream.expr.Expressible;
5151
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
52-
import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParser;
5352
import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
53+
import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
5454
import org.apache.solr.common.SolrException;
5555
import org.apache.solr.common.params.ModifiableSolrParams;
56+
import org.apache.solr.common.util.IOUtils;
5657
import org.apache.solr.handler.CatStream;
5758

5859
/** Supports stream command in the bin/solr script. */
@@ -62,8 +63,6 @@ public StreamTool(ToolRuntime runtime) {
6263
super(runtime);
6364
}
6465

65-
private final SolrClientCache solrClientCache = new SolrClientCache();
66-
6766
@Override
6867
public String getName() {
6968
return "stream";
@@ -166,14 +165,30 @@ public void runImpl(CommandLine cli) throws Exception {
166165
}
167166
}
168167

169-
PushBackStream pushBackStream;
170-
if (execution.equalsIgnoreCase("local")) {
171-
pushBackStream = doLocalMode(cli, expr);
172-
} else {
173-
pushBackStream = doRemoteMode(cli, expr);
168+
// Validate inputs before opening any connection to Solr.
169+
boolean local = execution.equalsIgnoreCase("local");
170+
if (!local) {
171+
if (!cli.hasOption(COLLECTION_OPTION)) {
172+
throw new IllegalStateException(
173+
"You must provide --name COLLECTION with --execution remote parameter.");
174+
}
175+
if (expr.toLowerCase(Locale.ROOT).contains("stdin(")) {
176+
throw new IllegalStateException(
177+
"The stdin() expression is only usable with --execution local.");
178+
}
174179
}
175180

181+
// a stream needs a context
182+
StreamContext streamContext = createStreamContext(cli);
183+
// create the stream
184+
PushBackStream pushBackStream = null;
176185
try {
186+
if (local) {
187+
pushBackStream = doLocalMode(expr, streamContext.getStreamFactory());
188+
} else {
189+
pushBackStream = doRemoteMode(expr, cli);
190+
}
191+
pushBackStream.setStreamContext(streamContext);
177192
pushBackStream.open();
178193

179194
if (outputHeaders == null) {
@@ -226,36 +241,60 @@ public void runImpl(CommandLine cli) throws Exception {
226241
}
227242
}
228243
} finally {
229-
pushBackStream.close();
230-
solrClientCache.close();
244+
if (pushBackStream != null) {
245+
pushBackStream.close();
246+
}
247+
streamContext.getSolrClientCache().close();
231248
}
232249

233250
echoIfVerbose("StreamTool -- Done.");
234251
}
235252

253+
private StreamContext createStreamContext(CommandLine cli) throws Exception {
254+
var jettyClientBuilder = new HttpJettySolrClient.Builder();
255+
String credentials = cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION);
256+
jettyClientBuilder.withOptionalBasicAuthCredentials(credentials);
257+
HttpJettySolrClient client = jettyClientBuilder.build();
258+
259+
// subclass so we can ensure our client is closed when the cache is closed
260+
var solrClientCache =
261+
new SolrClientCache(client) {
262+
@Override
263+
public synchronized void close() {
264+
super.close();
265+
client.close();
266+
}
267+
};
268+
269+
try {
270+
var solrConnection = CLIUtils.getSolrConnection(cli);
271+
echoIfVerbose("Connecting to Solr at " + solrConnection);
272+
273+
StreamContext streamContext = new StreamContext();
274+
streamContext.setSolrClientCache(solrClientCache);
275+
276+
StreamFactory streamFactory = new DefaultStreamFactory();
277+
streamFactory.withDefaultSolrConnection(solrConnection);
278+
streamContext.setStreamFactory(streamFactory);
279+
return streamContext;
280+
} catch (Exception e) {
281+
IOUtils.closeQuietly(solrClientCache);
282+
throw e;
283+
}
284+
}
285+
236286
/**
237287
* Runs a streaming expression in the local process of the CLI.
238288
*
239289
* <p>Running locally means that parallelization support or those expressions requiring access to
240290
* internal Solr capabilities will not function.
241291
*
242-
* @param cli The CLI invoking the call
243-
* @param expr The streaming expression to be parsed and in the context of the CLI process
292+
* @param expr The streaming expression to be parsed and run in the context of the CLI process
293+
* @param streamFactory The factory used to construct the streaming expression
244294
* @return A connection to the streaming expression that receives Tuples as they are emitted
245295
* locally.
246296
*/
247-
private PushBackStream doLocalMode(CommandLine cli, String expr) throws Exception {
248-
var solrConnection = CLIUtils.getSolrConnection(cli);
249-
echoIfVerbose("Connecting to Solr at " + solrConnection.toString());
250-
solrClientCache.setBasicAuthCredentials(
251-
cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION));
252-
solrClientCache.getCloudSolrClient(solrConnection);
253-
254-
TupleStream stream;
255-
PushBackStream pushBackStream;
256-
257-
StreamExpression streamExpression = StreamExpressionParser.parse(expr);
258-
StreamFactory streamFactory = new StreamFactory();
297+
private PushBackStream doLocalMode(String expr, StreamFactory streamFactory) throws Exception {
259298

260299
// stdin is ONLY available in the local mode, not in the remote mode as it
261300
// requires access to System.in
@@ -265,23 +304,7 @@ private PushBackStream doLocalMode(CommandLine cli, String expr) throws Exceptio
265304
// logic about where to read data from.
266305
streamFactory.withFunctionName("cat", LocalCatStream.class);
267306

268-
streamFactory.withDefaultSolrConnection(solrConnection);
269-
270-
Lang.register(streamFactory);
271-
272-
assert streamExpression != null;
273-
stream = streamFactory.constructStream(streamExpression);
274-
275-
pushBackStream = new PushBackStream(stream);
276-
277-
// Now we can run the stream and return the results.
278-
StreamContext streamContext = new StreamContext();
279-
streamContext.setSolrClientCache(solrClientCache);
280-
281-
// Output the headers
282-
pushBackStream.setStreamContext(streamContext);
283-
284-
return pushBackStream;
307+
return new PushBackStream(streamFactory.constructStream(expr));
285308
}
286309

287310
/**
@@ -291,35 +314,18 @@ private PushBackStream doLocalMode(CommandLine cli, String expr) throws Exceptio
291314
* <p>Running remotely allows you to use all the standard Streaming Expression capabilities as the
292315
* expression is running in a Solr environment.
293316
*
294-
* @param cli The CLI invoking the call
295317
* @param expr The streaming expression to be parsed and run remotely
318+
* @param cli The CLI invoking the call
296319
* @return A connection to the streaming expression that receives Tuples as they are emitted from
297320
* Solr /stream.
298321
*/
299-
private PushBackStream doRemoteMode(CommandLine cli, String expr) throws Exception {
322+
private PushBackStream doRemoteMode(String expr, CommandLine cli) throws Exception {
300323

301324
String solrUrl = CLIUtils.normalizeSolrUrl(cli);
302-
if (!cli.hasOption(COLLECTION_OPTION)) {
303-
throw new IllegalStateException(
304-
"You must provide --name COLLECTION with --execution remote parameter.");
305-
}
306325
String collection = cli.getOptionValue(COLLECTION_OPTION);
307326

308-
if (expr.toLowerCase(Locale.ROOT).contains("stdin(")) {
309-
throw new IllegalStateException(
310-
"The stdin() expression is only usable with --worker local set up.");
311-
}
312-
313-
final SolrStream solrStream =
314-
new SolrStream(solrUrl + "/solr/" + collection, params("qt", "/stream", "expr", expr));
315-
316-
String credentials = cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION);
317-
if (credentials != null) {
318-
String username = credentials.split(":")[0];
319-
String password = credentials.split(":")[1];
320-
solrStream.setCredentials(username, password);
321-
}
322-
return new PushBackStream(solrStream);
327+
return new PushBackStream(
328+
new SolrStream(solrUrl + "/solr/" + collection, params("qt", "/stream", "expr", expr)));
323329
}
324330

325331
private static ModifiableSolrParams params(String... params) {

0 commit comments

Comments
 (0)