Skip to content

Commit a122401

Browse files
authored
SOLR-17651: Make sure CLI unit tests don't call System.exit() (#3258)
This introduces class ToolRuntime which is initiated and passed as a context for all CLI tools. When running tests, a sub-classes is used instead and blocks calls to System.exit().
1 parent ddf91b2 commit a122401

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+764
-698
lines changed

gradle/validation/forbidden-apis/defaults.all.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ java.util.Collections#shuffle(java.util.List) @ Use shuffle(List, Random) instea
5757
java.util.Locale#forLanguageTag(java.lang.String) @ use new Locale.Builder().setLanguageTag(...).build() which has error handling
5858
java.util.Locale#toString() @ use Locale#toLanguageTag() for a standardized BCP47 locale name
5959

60+
61+
@defaultMessage Direct calls to force the JVM to quit are forbidden in server code.
62+
java.lang.System#exit(int)
63+
java.lang.Runtime#exit(int)
64+
6065
@defaultMessage Constructors for wrapper classes of Java primitives should be avoided in favor of the public static methods available or autoboxing
6166
java.lang.Integer#<init>(**)
6267
java.lang.Byte#<init>(**)

solr/CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ Other Changes
265265

266266
* SOLR-17716: Handle interrupted exception in SolrCores.waitAddPendingCoreOps. (Bruno Roustant)
267267

268+
* SOLR-17651: Add System.exit() in forbidden APIs, and make sure CLI unit tests never call it. (Pierre Salagnac)
269+
268270
================== 9.8.1 ==================
269271
Bug Fixes
270272
---------------------

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
package org.apache.solr.cli;
1919

20-
import java.io.PrintStream;
2120
import java.net.URI;
2221
import org.apache.commons.cli.CommandLine;
2322
import org.apache.commons.cli.Option;
@@ -46,12 +45,8 @@ public class ApiTool extends ToolBase {
4645
.desc("Send a GET request to a Solr API endpoint.")
4746
.build();
4847

49-
public ApiTool() {
50-
this(CLIO.getOutStream());
51-
}
52-
53-
public ApiTool(PrintStream stdout) {
54-
super(stdout);
48+
public ApiTool(ToolRuntime runtime) {
49+
super(runtime);
5550
}
5651

5752
@Override

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

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.apache.solr.cli;
1818

1919
import java.io.IOException;
20-
import java.io.PrintStream;
2120
import java.lang.invoke.MethodHandles;
2221
import java.nio.file.Files;
2322
import java.nio.file.Path;
@@ -144,12 +143,8 @@ public class AssertTool extends ToolBase {
144143
.longOpt("exitcode")
145144
.build();
146145

147-
public AssertTool() {
148-
this(CLIO.getOutStream());
149-
}
150-
151-
public AssertTool(PrintStream stdout) {
152-
super(stdout);
146+
public AssertTool(ToolRuntime runtime) {
147+
super(runtime);
153148
}
154149

155150
@Override
@@ -262,8 +257,8 @@ protected int runAssert(CommandLine cli) throws Exception {
262257
return ret;
263258
}
264259

265-
public static int assertSolrRunning(String url, String credentials) throws Exception {
266-
StatusTool status = new StatusTool();
260+
public int assertSolrRunning(String url, String credentials) throws Exception {
261+
StatusTool status = new StatusTool(runtime);
267262
try {
268263
status.waitToSeeSolrUp(url, credentials, timeoutMs, TimeUnit.MILLISECONDS);
269264
} catch (Exception se) {
@@ -280,8 +275,8 @@ public static int assertSolrRunning(String url, String credentials) throws Excep
280275
return 0;
281276
}
282277

283-
public static int assertSolrNotRunning(String url, String credentials) throws Exception {
284-
StatusTool status = new StatusTool();
278+
public int assertSolrNotRunning(String url, String credentials) throws Exception {
279+
StatusTool status = new StatusTool(runtime);
285280
long timeout =
286281
System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeoutMs, TimeUnit.MILLISECONDS);
287282
try (SolrClient solrClient = CLIUtils.getSolrClient(url, credentials)) {
@@ -316,7 +311,7 @@ public static int assertSolrNotRunning(String url, String credentials) throws Ex
316311
+ " seconds");
317312
}
318313

319-
public static int assertSolrRunningInCloudMode(String url, String credentials) throws Exception {
314+
public int assertSolrRunningInCloudMode(String url, String credentials) throws Exception {
320315
if (!isSolrRunningOn(url, credentials)) {
321316
return exitOrException(
322317
"Solr is not running on url "
@@ -332,8 +327,7 @@ public static int assertSolrRunningInCloudMode(String url, String credentials) t
332327
return 0;
333328
}
334329

335-
public static int assertSolrNotRunningInCloudMode(String url, String credentials)
336-
throws Exception {
330+
public int assertSolrNotRunningInCloudMode(String url, String credentials) throws Exception {
337331
if (!isSolrRunningOn(url, credentials)) {
338332
return exitOrException(
339333
"Solr is not running on url "
@@ -411,8 +405,8 @@ private static int exitOrException(String msg) throws AssertionFailureException
411405
}
412406
}
413407

414-
private static boolean isSolrRunningOn(String url, String credentials) throws Exception {
415-
StatusTool status = new StatusTool();
408+
private boolean isSolrRunningOn(String url, String credentials) throws Exception {
409+
StatusTool status = new StatusTool(runtime);
416410
try {
417411
status.waitToSeeSolrUp(url, credentials, timeoutMs, TimeUnit.MILLISECONDS);
418412
return true;

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

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import com.fasterxml.jackson.databind.node.ObjectNode;
2424
import java.io.Console;
2525
import java.io.IOException;
26-
import java.io.PrintStream;
2726
import java.net.URL;
2827
import java.nio.charset.StandardCharsets;
2928
import java.nio.file.Files;
@@ -101,12 +100,8 @@ public class AuthTool extends ToolBase {
101100
"This is where any authentication related configuration files, if any, would be placed. Defaults to $SOLR_HOME.")
102101
.build();
103102

104-
public AuthTool() {
105-
this(CLIO.getOutStream());
106-
}
107-
108-
public AuthTool(PrintStream stdout) {
109-
super(stdout);
103+
public AuthTool(ToolRuntime runtime) {
104+
super(runtime);
110105
}
111106

112107
@Override
@@ -155,7 +150,7 @@ private void ensureArgumentIsValidBooleanIfPresent(CommandLine cli, Option optio
155150
+ "] must be either true or false, but was ["
156151
+ value
157152
+ "]");
158-
SolrCLI.exit(1);
153+
runtime.exit(1);
159154
}
160155
}
161156
}
@@ -169,12 +164,12 @@ private void handleBasicAuth(CommandLine cli) throws Exception {
169164
case "enable":
170165
if (!prompt && !cli.hasOption(CommonCLIOptions.CREDENTIALS_OPTION)) {
171166
CLIO.out("Option --credentials or --prompt is required with enable.");
172-
SolrCLI.exit(1);
167+
runtime.exit(1);
173168
} else if (!prompt
174169
&& (cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION) == null
175170
|| !cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION).contains(":"))) {
176171
CLIO.out("Option --credentials is not in correct format.");
177-
SolrCLI.exit(1);
172+
runtime.exit(1);
178173
}
179174

180175
String zkHost = null;
@@ -190,7 +185,7 @@ private void handleBasicAuth(CommandLine cli) throws Exception {
190185
CLIO.out(
191186
"Couldn't get ZooKeeper host. Please make sure Solr is running in cloud mode, or a zk-host has been passed in.");
192187
}
193-
SolrCLI.exit(1);
188+
runtime.exit(1);
194189
}
195190
if (zkHost == null) {
196191
if (cli.hasOption(CommonCLIOptions.ZK_HOST_OPTION)) {
@@ -200,7 +195,7 @@ private void handleBasicAuth(CommandLine cli) throws Exception {
200195
CLIO.out(
201196
"Couldn't get ZooKeeper host. Please make sure Solr is running in cloud mode, or a zk-host has been passed in.");
202197
}
203-
SolrCLI.exit(1);
198+
runtime.exit(1);
204199
}
205200

206201
// check if security is already enabled or not
@@ -264,15 +259,15 @@ private void handleBasicAuth(CommandLine cli) throws Exception {
264259
CLIO.out(
265260
"Solr include file " + solrIncludeFilename + " doesn't exist or is not writeable.");
266261
printAuthEnablingInstructions(username, password);
267-
System.exit(0);
262+
runtime.exit(0);
268263
}
269264
String authConfDir = cli.getOptionValue(AUTH_CONF_DIR_OPTION);
270265
Path basicAuthConfFile = Path.of(authConfDir, "basicAuth.conf");
271266

272267
if (!Files.isWritable(basicAuthConfFile.getParent())) {
273268
CLIO.out("Cannot write to file: " + basicAuthConfFile.toAbsolutePath());
274269
printAuthEnablingInstructions(username, password);
275-
System.exit(0);
270+
runtime.exit(0);
276271
}
277272

278273
Files.writeString(
@@ -297,19 +292,19 @@ private void handleBasicAuth(CommandLine cli) throws Exception {
297292
"Solr include file " + solrIncludeFilename + " doesn't exist or is not writeable.");
298293
CLIO.out(
299294
"Security has been disabled. Please remove any SOLR_AUTH_TYPE or SOLR_AUTHENTICATION_OPTS configuration from solr.in.sh/solr.in.cmd.\n");
300-
System.exit(0);
295+
runtime.exit(0);
301296
}
302297

303298
// update the solr.in.sh file to comment out the necessary authentication lines
304299
updateIncludeFileDisableAuth(includeFile);
305300
return;
306301
default:
307302
CLIO.out("Valid auth commands are: enable, disable.");
308-
SolrCLI.exit(1);
303+
runtime.exit(1);
309304
}
310305

311306
CLIO.out("Options not understood.");
312-
SolrCLI.exit(1);
307+
runtime.exit(1);
313308
}
314309

315310
private void checkSecurityJsonExists(SolrZkClient zkClient)
@@ -320,7 +315,7 @@ private void checkSecurityJsonExists(SolrZkClient zkClient)
320315
CLIO.out(
321316
"Security is already enabled. You can disable it with 'bin/solr auth disable'. Existing security.json: \n"
322317
+ new String(oldSecurityBytes, StandardCharsets.UTF_8));
323-
SolrCLI.exit(1);
318+
runtime.exit(1);
324319
}
325320
}
326321
}
@@ -330,8 +325,8 @@ private void clearSecurityJson(CommandLine cli, boolean updateIncludeFileOnly) t
330325
if (!updateIncludeFileOnly) {
331326
zkHost = CLIUtils.getZkHost(cli);
332327
if (zkHost == null) {
333-
stdout.print("ZK Host not found. Solr should be running in cloud mode.");
334-
SolrCLI.exit(1);
328+
runtime.print("ZK Host not found. Solr should be running in cloud mode.");
329+
runtime.exit(1);
335330
}
336331

337332
echoIfVerbose("Uploading following security.json: {}");

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,7 @@ public static String getZkHost(CommandLine cli) throws Exception {
234234
new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH));
235235

236236
// convert raw JSON into user-friendly output
237-
StatusTool statusTool = new StatusTool();
238-
Map<String, Object> status = statusTool.reportStatus(systemInfo, solrClient);
237+
Map<String, Object> status = StatusTool.reportStatus(systemInfo, solrClient);
239238
@SuppressWarnings("unchecked")
240239
Map<String, Object> cloud = (Map<String, Object>) status.get("cloud");
241240
if (cloud != null) {

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
package org.apache.solr.cli;
1919

2020
import java.io.IOException;
21-
import java.io.PrintStream;
2221
import java.util.concurrent.TimeUnit;
2322
import org.apache.commons.cli.CommandLine;
2423
import org.apache.commons.cli.Option;
@@ -53,12 +52,8 @@ public class ClusterTool extends ToolBase {
5352
.desc("Set the property to this value.")
5453
.build();
5554

56-
public ClusterTool() {
57-
this(CLIO.getOutStream());
58-
}
59-
60-
public ClusterTool(PrintStream stdout) {
61-
super(stdout);
55+
public ClusterTool(ToolRuntime runtime) {
56+
super(runtime);
6257
}
6358

6459
@Override

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.solr.cli;
1818

19-
import java.io.PrintStream;
2019
import java.lang.invoke.MethodHandles;
2120
import java.nio.file.Files;
2221
import java.nio.file.Path;
@@ -50,12 +49,8 @@ public class ConfigSetDownloadTool extends ToolBase {
5049
.desc("Local directory with configs.")
5150
.build();
5251

53-
public ConfigSetDownloadTool() {
54-
this(CLIO.getOutStream());
55-
}
56-
57-
public ConfigSetDownloadTool(PrintStream stdout) {
58-
super(stdout);
52+
public ConfigSetDownloadTool(ToolRuntime runtime) {
53+
super(runtime);
5954
}
6055

6156
@Override

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.solr.cli;
1818

19-
import java.io.PrintStream;
2019
import java.lang.invoke.MethodHandles;
2120
import java.nio.file.Path;
2221
import java.nio.file.Paths;
@@ -52,12 +51,8 @@ public class ConfigSetUploadTool extends ToolBase {
5251
.desc("Local directory with configs.")
5352
.build();
5453

55-
public ConfigSetUploadTool() {
56-
this(CLIO.getOutStream());
57-
}
58-
59-
public ConfigSetUploadTool(PrintStream stdout) {
60-
super(stdout);
54+
public ConfigSetUploadTool(ToolRuntime runtime) {
55+
super(runtime);
6156
}
6257

6358
@Override

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
package org.apache.solr.cli;
1919

20-
import java.io.PrintStream;
2120
import java.util.HashMap;
2221
import java.util.Map;
2322
import org.apache.commons.cli.CommandLine;
@@ -72,12 +71,8 @@ public class ConfigTool extends ToolBase {
7271
.desc("Set the property to this value; accepts JSON objects and strings.")
7372
.build();
7473

75-
public ConfigTool() {
76-
this(CLIO.getOutStream());
77-
}
78-
79-
public ConfigTool(PrintStream stdout) {
80-
super(stdout);
74+
public ConfigTool(ToolRuntime runtime) {
75+
super(runtime);
8176
}
8277

8378
@Override

0 commit comments

Comments
 (0)