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 9da7d0495a8c..4a9c86fb8485 100644 --- a/solr/core/src/java/org/apache/solr/cli/ApiTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ApiTool.java @@ -43,7 +43,7 @@ public class ApiTool extends ToolBase { .argName("URL") .required() .desc("Send a GET request to a Solr API endpoint.") - .build(); + .get(); public ApiTool(ToolRuntime runtime) { super(runtime); @@ -77,7 +77,7 @@ protected String callGet(String url, String credentials) throws Exception { try (var solrClient = CLIUtils.getSolrClient(solrUrl, credentials)) { // For path parameter we need the path without the root so from the second / char // (because root can be configured) - // E.g URL is http://localhost:8983/solr/admin/info/system path is + // E.g. URL is http://localhost:8983/solr/admin/info/system path is // /solr/admin/info/system and the path without root is /admin/info/system var req = new GenericSolrRequest( diff --git a/solr/core/src/java/org/apache/solr/cli/AssertTool.java b/solr/core/src/java/org/apache/solr/cli/AssertTool.java index e0349cbf3281..928065f0a798 100644 --- a/solr/core/src/java/org/apache/solr/cli/AssertTool.java +++ b/solr/core/src/java/org/apache/solr/cli/AssertTool.java @@ -45,10 +45,10 @@ public class AssertTool extends ToolBase { private static Long timeoutMs = 1000L; private static final Option IS_NOT_ROOT_OPTION = - Option.builder().desc("Asserts that we are NOT the root user.").longOpt("not-root").build(); + Option.builder().desc("Asserts that we are NOT the root user.").longOpt("not-root").get(); private static final Option IS_ROOT_OPTION = - Option.builder().desc("Asserts that we are the root user.").longOpt("root").build(); + Option.builder().desc("Asserts that we are the root user.").longOpt("root").get(); private static final OptionGroup ROOT_OPTION = new OptionGroup().addOption(IS_NOT_ROOT_OPTION).addOption(IS_ROOT_OPTION); @@ -59,7 +59,7 @@ public class AssertTool extends ToolBase { .longOpt("not-started") .hasArg() .argName("url") - .build(); + .get(); private static final Option IS_RUNNING_ON_OPTION = Option.builder() @@ -67,7 +67,7 @@ public class AssertTool extends ToolBase { .longOpt("started") .hasArg() .argName("url") - .build(); + .get(); private static final OptionGroup RUNNING_OPTION = new OptionGroup().addOption(IS_NOT_RUNNING_ON_OPTION).addOption(IS_RUNNING_ON_OPTION); @@ -78,7 +78,7 @@ public class AssertTool extends ToolBase { .longOpt("same-user") .hasArg() .argName("directory") - .build(); + .get(); private static final Option DIRECTORY_EXISTS_OPTION = Option.builder() @@ -86,7 +86,7 @@ public class AssertTool extends ToolBase { .longOpt("exists") .hasArg() .argName("directory") - .build(); + .get(); private static final Option DIRECTORY_NOT_EXISTS_OPTION = Option.builder() @@ -94,7 +94,7 @@ public class AssertTool extends ToolBase { .longOpt("not-exists") .hasArg() .argName("directory") - .build(); + .get(); private static final OptionGroup DIRECTORY_OPTION = new OptionGroup().addOption(DIRECTORY_EXISTS_OPTION).addOption(DIRECTORY_NOT_EXISTS_OPTION); @@ -106,7 +106,7 @@ public class AssertTool extends ToolBase { .longOpt("cloud") .hasArg() .argName("url") - .build(); + .get(); private static final Option IS_NOT_CLOUD_OPTION = Option.builder() @@ -115,7 +115,7 @@ public class AssertTool extends ToolBase { .longOpt("not-cloud") .hasArg() .argName("url") - .build(); + .get(); private static final OptionGroup CLOUD_OPTION = new OptionGroup().addOption(IS_CLOUD_OPTION).addOption(IS_NOT_CLOUD_OPTION); @@ -126,7 +126,7 @@ public class AssertTool extends ToolBase { .longOpt("message") .hasArg() .argName("message") - .build(); + .get(); private static final Option TIMEOUT_OPTION = Option.builder() @@ -135,13 +135,13 @@ public class AssertTool extends ToolBase { .hasArg() .type(Long.class) .argName("ms") - .build(); + .get(); private static final Option EXIT_CODE_OPTION = Option.builder() .desc("Return an exit code instead of printing error message on assert fail.") .longOpt("exitcode") - .build(); + .get(); public AssertTool(ToolRuntime runtime) { super(runtime); @@ -292,9 +292,10 @@ public int assertSolrNotRunning(String url, String credentials) throws Exception status.waitToSeeSolrUp(url, credentials, 1, TimeUnit.SECONDS); try { log.debug("Solr still up. Waiting before trying again to see if it was stopped"); - Thread.sleep(1000L); + TimeUnit.MILLISECONDS.sleep(1000L); } catch (InterruptedException interrupted) { - timeout = 0; // stop looping + Thread.currentThread().interrupt(); + break; } } catch (Exception se) { if (CLIUtils.exceptionIsAuthRelated(se)) { @@ -312,7 +313,7 @@ public int assertSolrNotRunning(String url, String credentials) throws Exception } public int assertSolrRunningInCloudMode(String url, String credentials) throws Exception { - if (!isSolrRunningOn(url, credentials)) { + if (isSolrStoppedOn(url, credentials)) { return exitOrException( "Solr is not running on url " + url @@ -328,7 +329,7 @@ public int assertSolrRunningInCloudMode(String url, String credentials) throws E } public int assertSolrNotRunningInCloudMode(String url, String credentials) throws Exception { - if (!isSolrRunningOn(url, credentials)) { + if (isSolrStoppedOn(url, credentials)) { return exitOrException( "Solr is not running on url " + url @@ -344,8 +345,9 @@ public int assertSolrNotRunningInCloudMode(String url, String credentials) throw } public static int sameUser(String directory) throws Exception { - if (Files.exists(Path.of(directory))) { - String userForDir = userForDir(Path.of(directory)); + Path path = Path.of(directory); + if (Files.exists(path)) { + String userForDir = userForDir(path); if (!currentUser().equals(userForDir)) { return exitOrException("Must run as user " + userForDir + ". We are " + currentUser()); } @@ -405,16 +407,16 @@ private static int exitOrException(String msg) throws AssertionFailureException } } - private boolean isSolrRunningOn(String url, String credentials) throws Exception { + private boolean isSolrStoppedOn(String url, String credentials) throws Exception { StatusTool status = new StatusTool(runtime); try { status.waitToSeeSolrUp(url, credentials, timeoutMs, TimeUnit.MILLISECONDS); - return true; + return false; } catch (Exception se) { if (CLIUtils.exceptionIsAuthRelated(se)) { throw se; } - return false; + return true; } } diff --git a/solr/core/src/java/org/apache/solr/cli/AuthTool.java b/solr/core/src/java/org/apache/solr/cli/AuthTool.java index 642411808fd3..5c19ac300d7f 100644 --- a/solr/core/src/java/org/apache/solr/cli/AuthTool.java +++ b/solr/core/src/java/org/apache/solr/cli/AuthTool.java @@ -49,7 +49,7 @@ public class AuthTool extends ToolBase { .hasArg() .desc( "The authentication mechanism to enable (currently only basicAuth). Defaults to 'basicAuth'.") - .build(); + .get(); private static final Option PROMPT_OPTION = Option.builder() @@ -58,7 +58,7 @@ public class AuthTool extends ToolBase { .type(Boolean.class) .desc( "Prompts the user to provide the credentials. Use either --credentials or --prompt, not both.") - .build(); + .get(); private static final Option BLOCK_UNKNOWN_OPTION = Option.builder() @@ -67,7 +67,7 @@ public class AuthTool extends ToolBase { .hasArg() .argName("true|false") .type(Boolean.class) - .build(); + .get(); private static final Option SOLR_INCLUDE_FILE_OPTION = Option.builder() @@ -78,7 +78,7 @@ public class AuthTool extends ToolBase { .desc( "The Solr include file which contains overridable environment variables for configuring Solr configurations. Defaults to solr.in." + (CLIUtils.isWindows() ? ".cmd" : ".sh")) - .build(); + .get(); private static final Option UPDATE_INCLUDE_FILE_OPTION = Option.builder() @@ -88,7 +88,7 @@ public class AuthTool extends ToolBase { + " authentication (i.e. don't update security.json).") .hasArg() .type(Boolean.class) - .build(); + .get(); private static final Option AUTH_CONF_DIR_OPTION = Option.builder() @@ -98,7 +98,7 @@ public class AuthTool extends ToolBase { .required() .desc( "This is where any authentication related configuration files, if any, would be placed. Defaults to $SOLR_HOME.") - .build(); + .get(); public AuthTool(ToolRuntime runtime) { super(runtime); @@ -111,16 +111,20 @@ public String getName() { @Override public String getUsage() { - return "\n bin/solr auth enable [--type basicAuth] --credentials user:pass [--block-unknown ] [--update-include-file-only ] [-v]\n" - + " bin/solr auth enable [--type basicAuth] --prompt [--block-unknown ] [--update-include-file-only ] [-v]\n" - + " bin/solr auth disable [--update-include-file-only ] [-v]\n"; + return """ + + bin/solr auth enable [--type basicAuth] --credentials user:pass [--block-unknown ] [--update-include-file-only ] [-v] + bin/solr auth enable [--type basicAuth] --prompt [--block-unknown ] [--update-include-file-only ] [-v] + bin/solr auth disable [--update-include-file-only ] [-v] + """; } @Override public String getHeader() { - return "Updates or enables/disables authentication. Must be run on the Solr server itself.\n" - + "\n" - + "List of options:"; + return """ + Updates or enables/disables authentication. Must be run on the Solr server itself. + + List of options:"""; } List authenticationVariables = @@ -162,142 +166,146 @@ private void handleBasicAuth(CommandLine cli) throws Exception { Boolean.parseBoolean(cli.getOptionValue(UPDATE_INCLUDE_FILE_OPTION, "false")); switch (cmd) { case "enable": - if (!prompt && !cli.hasOption(CommonCLIOptions.CREDENTIALS_OPTION)) { - CLIO.out("Option --credentials or --prompt is required with enable."); - runtime.exit(1); - } else if (!prompt - && (cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION) == null - || !cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION).contains(":"))) { - CLIO.out("Option --credentials is not in correct format."); - runtime.exit(1); - } - - String zkHost = null; - - if (!updateIncludeFileOnly) { - try { - zkHost = CLIUtils.getZkHost(cli); - } catch (Exception ex) { - if (cli.hasOption(CommonCLIOptions.ZK_HOST_OPTION)) { - CLIO.out( - "Couldn't get ZooKeeper host. Please make sure that ZooKeeper is running and the correct zk-host has been passed in."); - } else { - CLIO.out( - "Couldn't get ZooKeeper host. Please make sure Solr is running in cloud mode, or a zk-host has been passed in."); - } + { + if (!prompt && !cli.hasOption(CommonCLIOptions.CREDENTIALS_OPTION)) { + CLIO.out("Option --credentials or --prompt is required with enable."); + runtime.exit(1); + } else if (!prompt + && (cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION) == null + || !cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION).contains(":"))) { + CLIO.out("Option --credentials is not in correct format."); runtime.exit(1); } - if (zkHost == null) { - if (cli.hasOption(CommonCLIOptions.ZK_HOST_OPTION)) { - CLIO.out( - "Couldn't get ZooKeeper host. Please make sure that ZooKeeper is running and the correct zk-host has been passed in."); - } else { - CLIO.out( - "Couldn't get ZooKeeper host. Please make sure Solr is running in cloud mode, or a zk-host has been passed in."); + + String zkHost = null; + + if (!updateIncludeFileOnly) { + try { + zkHost = CLIUtils.getZkHost(cli); + } catch (Exception ex) { + if (cli.hasOption(CommonCLIOptions.ZK_HOST_OPTION)) { + CLIO.out( + "Couldn't get ZooKeeper host. Please make sure that ZooKeeper is running and the correct zk-host has been passed in."); + } else { + CLIO.out( + "Couldn't get ZooKeeper host. Please make sure Solr is running in cloud mode, or a zk-host has been passed in."); + } + runtime.exit(1); + } + if (zkHost == null) { + if (cli.hasOption(CommonCLIOptions.ZK_HOST_OPTION)) { + CLIO.out( + "Couldn't get ZooKeeper host. Please make sure that ZooKeeper is running and the correct zk-host has been passed in."); + } else { + CLIO.out( + "Couldn't get ZooKeeper host. Please make sure Solr is running in cloud mode, or a zk-host has been passed in."); + } + runtime.exit(1); + } + + // check if security is already enabled or not + try (SolrZkClient zkClient = CLIUtils.getSolrZkClient(cli, zkHost)) { + checkSecurityJsonExists(zkClient); } - runtime.exit(1); } - // check if security is already enabled or not - try (SolrZkClient zkClient = CLIUtils.getSolrZkClient(cli, zkHost)) { - checkSecurityJsonExists(zkClient); + String username, password; + if (cli.hasOption(CommonCLIOptions.CREDENTIALS_OPTION)) { + String credentials = cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION); + username = credentials.split(":")[0]; + password = credentials.split(":")[1]; + } else { + Console console = System.console(); + // keep prompting until they've entered a non-empty username & password + do { + username = console.readLine("Enter username: "); + } while (username == null || username.trim().isEmpty()); + username = username.trim(); + + do { + password = new String(console.readPassword("Enter password: ")); + } while (password.isEmpty()); } - } - String username, password; - if (cli.hasOption(CommonCLIOptions.CREDENTIALS_OPTION)) { - String credentials = cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION); - username = credentials.split(":")[0]; - password = credentials.split(":")[1]; - } else { - Console console = System.console(); - // keep prompting until they've entered a non-empty username & password - do { - username = console.readLine("Enter username: "); - } while (username == null || username.trim().length() == 0); - username = username.trim(); - - do { - password = new String(console.readPassword("Enter password: ")); - } while (password.length() == 0); - } + boolean blockUnknown = + Boolean.parseBoolean(cli.getOptionValue(BLOCK_UNKNOWN_OPTION, "true")); - boolean blockUnknown = - Boolean.parseBoolean(cli.getOptionValue(BLOCK_UNKNOWN_OPTION, "true")); + String resourceName = "security.json"; + final URL resource = SolrCore.class.getClassLoader().getResource(resourceName); + if (null == resource) { + throw new IllegalArgumentException("invalid resource name: " + resourceName); + } - String resourceName = "security.json"; - final URL resource = SolrCore.class.getClassLoader().getResource(resourceName); - if (null == resource) { - throw new IllegalArgumentException("invalid resource name: " + resourceName); - } + ObjectMapper mapper = new ObjectMapper(); + JsonNode securityJson1 = mapper.readTree(resource.openStream()); + ((ObjectNode) securityJson1).put("blockUnknown", blockUnknown); + JsonNode credentialsNode = securityJson1.get("authentication").get("credentials"); + ((ObjectNode) credentialsNode) + .put(username, Sha256AuthenticationProvider.getSaltedHashedValue(password)); + JsonNode userRoleNode = securityJson1.get("authorization").get("user-role"); + String[] predefinedRoles = {"superadmin", "admin", "search", "index"}; + ArrayNode rolesNode = mapper.createArrayNode(); + for (String role : predefinedRoles) { + rolesNode.add(role); + } + ((ObjectNode) userRoleNode).set(username, rolesNode); + String securityJson = securityJson1.toPrettyString(); - ObjectMapper mapper = new ObjectMapper(); - JsonNode securityJson1 = mapper.readTree(resource.openStream()); - ((ObjectNode) securityJson1).put("blockUnknown", blockUnknown); - JsonNode credentialsNode = securityJson1.get("authentication").get("credentials"); - ((ObjectNode) credentialsNode) - .put(username, Sha256AuthenticationProvider.getSaltedHashedValue(password)); - JsonNode userRoleNode = securityJson1.get("authorization").get("user-role"); - String[] predefinedRoles = {"superadmin", "admin", "search", "index"}; - ArrayNode rolesNode = mapper.createArrayNode(); - for (String role : predefinedRoles) { - rolesNode.add(role); - } - ((ObjectNode) userRoleNode).set(username, rolesNode); - String securityJson = securityJson1.toPrettyString(); + if (!updateIncludeFileOnly) { + echoIfVerbose("Uploading following security.json: " + securityJson); + try (SolrZkClient zkClient = CLIUtils.getSolrZkClient(cli, zkHost)) { + zkClient.setData("/security.json", securityJson.getBytes(StandardCharsets.UTF_8)); + } + } - if (!updateIncludeFileOnly) { - echoIfVerbose("Uploading following security.json: " + securityJson); - try (SolrZkClient zkClient = CLIUtils.getSolrZkClient(cli, zkHost)) { - zkClient.setData("/security.json", securityJson.getBytes(StandardCharsets.UTF_8)); + String solrIncludeFilename = cli.getOptionValue(SOLR_INCLUDE_FILE_OPTION); + Path includeFile = Path.of(solrIncludeFilename); + if (Files.notExists(includeFile) || !Files.isWritable(includeFile)) { + CLIO.out( + "Solr include file " + solrIncludeFilename + " doesn't exist or is not writeable."); + printAuthEnablingInstructions(username, password); + runtime.exit(0); } - } + String authConfDir = cli.getOptionValue(AUTH_CONF_DIR_OPTION); + Path basicAuthConfFile = Path.of(authConfDir, "basicAuth.conf"); - String solrIncludeFilename = cli.getOptionValue(SOLR_INCLUDE_FILE_OPTION); - Path includeFile = Path.of(solrIncludeFilename); - if (Files.notExists(includeFile) || !Files.isWritable(includeFile)) { - CLIO.out( - "Solr include file " + solrIncludeFilename + " doesn't exist or is not writeable."); - printAuthEnablingInstructions(username, password); - runtime.exit(0); - } - String authConfDir = cli.getOptionValue(AUTH_CONF_DIR_OPTION); - Path basicAuthConfFile = Path.of(authConfDir, "basicAuth.conf"); + if (!Files.isWritable(basicAuthConfFile.getParent())) { + CLIO.out("Cannot write to file: " + basicAuthConfFile.toAbsolutePath()); + printAuthEnablingInstructions(username, password); + runtime.exit(0); + } - if (!Files.isWritable(basicAuthConfFile.getParent())) { - CLIO.out("Cannot write to file: " + basicAuthConfFile.toAbsolutePath()); - printAuthEnablingInstructions(username, password); - runtime.exit(0); + Files.writeString( + basicAuthConfFile, + "httpBasicAuthUser=" + username + "\nhttpBasicAuthPassword=" + password, + StandardCharsets.UTF_8); + + // update the solr.in.sh file to contain the necessary authentication lines + updateIncludeFileEnableAuth(includeFile, basicAuthConfFile); + final String successMessage = + String.format( + Locale.ROOT, "Successfully enabled basic auth with username [%s].", username); + echo(successMessage); + return; } - - Files.writeString( - basicAuthConfFile, - "httpBasicAuthUser=" + username + "\nhttpBasicAuthPassword=" + password, - StandardCharsets.UTF_8); - - // update the solr.in.sh file to contain the necessary authentication lines - updateIncludeFileEnableAuth(includeFile, basicAuthConfFile); - final String successMessage = - String.format( - Locale.ROOT, "Successfully enabled basic auth with username [%s].", username); - echo(successMessage); - return; case "disable": - clearSecurityJson(cli, updateIncludeFileOnly); - - solrIncludeFilename = cli.getOptionValue(SOLR_INCLUDE_FILE_OPTION); - includeFile = Path.of(solrIncludeFilename); - if (Files.notExists(includeFile) || !Files.isWritable(includeFile)) { - CLIO.out( - "Solr include file " + solrIncludeFilename + " doesn't exist or is not writeable."); - CLIO.out( - "Security has been disabled. Please remove any SOLR_AUTH_TYPE or SOLR_AUTHENTICATION_OPTS configuration from solr.in.sh/solr.in.cmd.\n"); - runtime.exit(0); - } + { + clearSecurityJson(cli, updateIncludeFileOnly); + + String solrIncludeFilename = cli.getOptionValue(SOLR_INCLUDE_FILE_OPTION); + Path includeFile = Path.of(solrIncludeFilename); + if (Files.notExists(includeFile) || !Files.isWritable(includeFile)) { + CLIO.out( + "Solr include file " + solrIncludeFilename + " doesn't exist or is not writeable."); + CLIO.out( + "Security has been disabled. Please remove any SOLR_AUTH_TYPE or SOLR_AUTHENTICATION_OPTS configuration from solr.in.sh/solr.in.cmd.\n"); + runtime.exit(0); + } - // update the solr.in.sh file to comment out the necessary authentication lines - updateIncludeFileDisableAuth(includeFile); - return; + // update the solr.in.sh file to comment out the necessary authentication lines + updateIncludeFileDisableAuth(includeFile); + return; + } default: CLIO.out("Valid auth commands are: enable, disable."); runtime.exit(1); @@ -438,12 +446,10 @@ public void runImpl(CommandLine cli) throws Exception { String type = cli.getOptionValue(TYPE_OPTION, "basicAuth"); // switch structure is here to support future auth options like oAuth - switch (type) { - case "basicAuth": - handleBasicAuth(cli); - break; - default: - throw new IllegalStateException("Only type=basicAuth supported at the moment."); + if (type.equals("basicAuth")) { + handleBasicAuth(cli); + } else { + throw new IllegalStateException("Only type=basicAuth supported at the moment."); } } } diff --git a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java index 36baeb8357f8..1c05377e889e 100644 --- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java +++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java @@ -198,7 +198,6 @@ public static String normalizeSolrUrl(CommandLine cli) throws Exception { + "."); } else { try (CloudSolrClient cloudSolrClient = getCloudSolrClient(zkHost)) { - cloudSolrClient.connect(); Set liveNodes = cloudSolrClient.getClusterState().getLiveNodes(); if (liveNodes.isEmpty()) throw new IllegalStateException( @@ -262,7 +261,7 @@ public static String getZkHost(CommandLine cli) throws Exception { return zkHost; } - public static SolrZkClient getSolrZkClient(CommandLine cli, String zkHost) throws Exception { + public static SolrZkClient getSolrZkClient(CommandLine cli, String zkHost) { if (zkHost == null) { throw new IllegalStateException( "Solr at " @@ -322,7 +321,6 @@ public static boolean safeCheckCollectionExists( return exists; } - @SuppressWarnings("unchecked") public static boolean safeCheckCoreExists(String solrUrl, String coreName, String credentials) { boolean exists = false; try (var solrClient = getSolrClient(solrUrl, credentials)) { @@ -330,8 +328,13 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin final long startWaitAt = System.nanoTime(); do { if (wait) { - final int clamPeriodForStatusPollMs = 1000; - Thread.sleep(clamPeriodForStatusPollMs); + final int pollIntervalMs = 1000; + try { + TimeUnit.MILLISECONDS.sleep(pollIntervalMs); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; + } } final var coreStatusReq = new CoresApi.GetCoreStatus(coreName); final var coreStatusRsp = coreStatusReq.process(solrClient); diff --git a/solr/core/src/java/org/apache/solr/cli/ClusterTool.java b/solr/core/src/java/org/apache/solr/cli/ClusterTool.java index 7f0a5bbb586b..54626e2b7db7 100644 --- a/solr/core/src/java/org/apache/solr/cli/ClusterTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ClusterTool.java @@ -42,7 +42,7 @@ public class ClusterTool extends ToolBase { .argName("PROPERTY") .required() .desc("Name of the Cluster property to apply the action to, such as: 'urlScheme'.") - .build(); + .get(); private static final Option VALUE_OPTION = Option.builder() @@ -50,7 +50,7 @@ public class ClusterTool extends ToolBase { .hasArg() .argName("VALUE") .desc("Set the property to this value.") - .build(); + .get(); public ClusterTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/CommonCLIOptions.java b/solr/core/src/java/org/apache/solr/cli/CommonCLIOptions.java index e6425abe1f37..d0b774030715 100644 --- a/solr/core/src/java/org/apache/solr/cli/CommonCLIOptions.java +++ b/solr/core/src/java/org/apache/solr/cli/CommonCLIOptions.java @@ -24,10 +24,10 @@ public final class CommonCLIOptions { private CommonCLIOptions() {} public static final Option VERBOSE_OPTION = - Option.builder("v").longOpt("verbose").desc("Enable verbose command output.").build(); + Option.builder("v").longOpt("verbose").desc("Enable verbose command output.").get(); public static final Option HELP_OPTION = - Option.builder("h").longOpt("help").desc("Print this message.").build(); + Option.builder("h").longOpt("help").desc("Print this message.").get(); public static final Option ZK_HOST_OPTION = Option.builder("z") @@ -38,7 +38,7 @@ private CommonCLIOptions() {} "Zookeeper connection string; unnecessary if ZK_HOST is defined in solr.in.sh; otherwise, defaults to " + DefaultValues.ZK_HOST + '.') - .build(); + .get(); public static final Option SOLR_URL_OPTION = Option.builder("s") @@ -49,10 +49,10 @@ private CommonCLIOptions() {} "Base Solr URL, which can be used to determine the zk-host if that's not known; defaults to: " + CLIUtils.getDefaultSolrUrl() + '.') - .build(); + .get(); public static final Option RECURSIVE_OPTION = - Option.builder("r").longOpt("recursive").desc("Apply the command recursively.").build(); + Option.builder("r").longOpt("recursive").desc("Apply the command recursively.").get(); public static final Option CREDENTIALS_OPTION = Option.builder("u") @@ -61,7 +61,7 @@ private CommonCLIOptions() {} .argName("credentials") .desc( "Credentials in the format username:password. Example: --credentials solr:SolrRocks") - .build(); + .get(); public static final class DefaultValues { diff --git a/solr/core/src/java/org/apache/solr/cli/ConfigSetDownloadTool.java b/solr/core/src/java/org/apache/solr/cli/ConfigSetDownloadTool.java index 7810d9745862..5623794626cb 100644 --- a/solr/core/src/java/org/apache/solr/cli/ConfigSetDownloadTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ConfigSetDownloadTool.java @@ -37,7 +37,7 @@ public class ConfigSetDownloadTool extends ToolBase { .argName("NAME") .required() .desc("Configset name in ZooKeeper.") - .build(); + .get(); private static final Option CONF_DIR_OPTION = Option.builder("d") @@ -46,7 +46,7 @@ public class ConfigSetDownloadTool extends ToolBase { .argName("DIR") .required() .desc("Local directory with configs.") - .build(); + .get(); public ConfigSetDownloadTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/ConfigSetUploadTool.java b/solr/core/src/java/org/apache/solr/cli/ConfigSetUploadTool.java index 7088e8919bf5..fdd2380f3a19 100644 --- a/solr/core/src/java/org/apache/solr/cli/ConfigSetUploadTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ConfigSetUploadTool.java @@ -39,7 +39,7 @@ public class ConfigSetUploadTool extends ToolBase { .argName("NAME") .required() .desc("Configset name in ZooKeeper.") - .build(); + .get(); private static final Option CONF_DIR_OPTION = Option.builder("d") @@ -48,7 +48,7 @@ public class ConfigSetUploadTool extends ToolBase { .argName("DIR") .required() .desc("Local directory with configs.") - .build(); + .get(); public ConfigSetUploadTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/ConfigTool.java b/solr/core/src/java/org/apache/solr/cli/ConfigTool.java index ce7b644c4575..a4145168165b 100644 --- a/solr/core/src/java/org/apache/solr/cli/ConfigTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ConfigTool.java @@ -43,7 +43,7 @@ public class ConfigTool extends ToolBase { .argName("NAME") .required() .desc("Name of the collection.") - .build(); + .get(); private static final Option ACTION_OPTION = Option.builder("a") @@ -52,7 +52,7 @@ public class ConfigTool extends ToolBase { .argName("ACTION") .desc( "Config API action, one of: set-property, unset-property, set-user-property, unset-user-property; default is 'set-property'.") - .build(); + .get(); private static final Option PROPERTY_OPTION = Option.builder() @@ -62,7 +62,7 @@ public class ConfigTool extends ToolBase { .required() .desc( "Name of the Config API property to apply the action to, such as: 'updateHandler.autoSoftCommit.maxTime'.") - .build(); + .get(); private static final Option VALUE_OPTION = Option.builder("v") @@ -70,7 +70,7 @@ public class ConfigTool extends ToolBase { .hasArg() .argName("VALUE") .desc("Set the property to this value; accepts JSON objects and strings.") - .build(); + .get(); public ConfigTool(ToolRuntime runtime) { super(runtime); @@ -100,7 +100,7 @@ public void runImpl(CommandLine cli) throws Exception { String property = cli.getOptionValue(PROPERTY_OPTION); String value = cli.getOptionValue(VALUE_OPTION); - // value is required unless the property is one of the unset- type. + // value is required unless the property is one of the "unset-" type. if (!action.contains("unset-") && value == null) { throw new MissingArgumentException("'value' is a required option."); } diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java index 0226471cd13c..33ab28ef383c 100644 --- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java @@ -55,7 +55,7 @@ public class CreateTool extends ToolBase { .argName("NAME") .required() .desc("Name of collection or core to create.") - .build(); + .get(); private static final Option SHARDS_OPTION = Option.builder("sh") @@ -64,7 +64,7 @@ public class CreateTool extends ToolBase { .argName("#") .type(Integer.class) .desc("Number of shards; default is 1.") - .build(); + .get(); private static final Option REPLICATION_FACTOR_OPTION = Option.builder("rf") @@ -74,7 +74,7 @@ public class CreateTool extends ToolBase { .type(Integer.class) .desc( "Number of copies of each document across the collection (replicas per shard); default is 1.") - .build(); + .get(); private static final Option CONF_DIR_OPTION = Option.builder("d") @@ -85,7 +85,7 @@ public class CreateTool extends ToolBase { "Configuration directory to copy when creating the new collection; default is " + DefaultValues.DEFAULT_CONFIG_SET + '.') - .build(); + .get(); private static final Option CONF_NAME_OPTION = Option.builder("n") @@ -93,7 +93,7 @@ public class CreateTool extends ToolBase { .hasArg() .argName("NAME") .desc("Configuration name; default is the collection name.") - .build(); + .get(); public CreateTool(ToolRuntime runtime) { super(runtime); @@ -106,25 +106,23 @@ public String getName() { @Override public String getHeader() { - return "Creates a core or collection depending on whether Solr is running in standalone (core) or SolrCloud mode (collection).\n" - + "If you are using standalone mode you must run this command on the Solr server itself.\n" - + "\n" - + "List of options:"; + return """ + Creates a core or collection depending on whether Solr is running in standalone (core) or SolrCloud mode (collection). + If you are using standalone mode you must run this command on the Solr server itself. + + List of options:"""; } @Override public Options getOptions() { - Options opts = - super.getOptions() - .addOption(COLLECTION_NAME_OPTION) - .addOption(SHARDS_OPTION) - .addOption(REPLICATION_FACTOR_OPTION) - .addOption(CONF_DIR_OPTION) - .addOption(CONF_NAME_OPTION) - .addOption(CommonCLIOptions.CREDENTIALS_OPTION) - .addOptionGroup(getConnectionOptions()); - - return opts; + return super.getOptions() + .addOption(COLLECTION_NAME_OPTION) + .addOption(SHARDS_OPTION) + .addOption(REPLICATION_FACTOR_OPTION) + .addOption(CONF_DIR_OPTION) + .addOption(CONF_NAME_OPTION) + .addOption(CommonCLIOptions.CREDENTIALS_OPTION) + .addOptionGroup(getConnectionOptions()); } @Override @@ -212,7 +210,6 @@ protected void createCollection(CommandLine cli) throws Exception { String zkHost = CLIUtils.getZkHost(cli); echoIfVerbose("Connecting to ZooKeeper at " + zkHost); try (CloudSolrClient cloudSolrClient = CLIUtils.getCloudSolrClient(zkHost, builder)) { - cloudSolrClient.connect(); createCollection(cloudSolrClient, cli); } } @@ -340,7 +337,7 @@ private void printDefaultConfigsetWarningIfNecessary(CommandLine cli) { final String confName = cli.getOptionValue(CONF_NAME_OPTION, ""); if (confDirectoryName.equals("_default") - && (confName.equals("") || confName.equals("_default"))) { + && (confName.isEmpty() || confName.equals("_default"))) { final String collectionName = cli.getOptionValue(COLLECTION_NAME_OPTION); final String solrUrl = cli.getOptionValue(CommonCLIOptions.SOLR_URL_OPTION, CLIUtils.getDefaultSolrUrl()); diff --git a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java index 3675098f8eed..2610e2af18d2 100644 --- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java +++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java @@ -50,21 +50,21 @@ public class DeleteTool extends ToolBase { .argName("NAME") .required() .desc("Name of the core / collection to delete.") - .build(); + .get(); private static final Option DELETE_CONFIG_OPTION = Option.builder() .longOpt("delete-config") .desc( "Flag to indicate if the underlying configuration directory for a collection should also be deleted; default is true.") - .build(); + .get(); private static final Option FORCE_OPTION = Option.builder("f") .longOpt("force") .desc( "Skip safety checks when deleting the configuration directory used by a collection.") - .build(); + .get(); public DeleteTool(ToolRuntime runtime) { super(runtime); @@ -77,10 +77,11 @@ public String getName() { @Override public String getHeader() { - return "Deletes a collection or core depending on whether Solr is running in SolrCloud or standalone mode. " - + "Deleting a collection does not delete it's configuration unless you pass in the --delete-config flag.\n" - + "\n" - + "List of options:"; + return """ + Deletes a collection or core depending on whether Solr is running in SolrCloud or standalone mode. \ + Deleting a collection does not delete it's configuration unless you pass in the --delete-config flag. + + List of options:"""; } @Override @@ -116,7 +117,6 @@ protected void deleteCollection(CommandLine cli) throws Exception { String zkHost = CLIUtils.getZkHost(cli); try (CloudSolrClient cloudSolrClient = CLIUtils.getCloudSolrClient(zkHost, builder)) { echoIfVerbose("Connecting to ZooKeeper at " + zkHost); - cloudSolrClient.connect(); deleteCollection(cloudSolrClient, cli); } } diff --git a/solr/core/src/java/org/apache/solr/cli/ExportTool.java b/solr/core/src/java/org/apache/solr/cli/ExportTool.java index 57336d2f7f94..6fd43b52a1c8 100644 --- a/solr/core/src/java/org/apache/solr/cli/ExportTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ExportTool.java @@ -93,7 +93,7 @@ public class ExportTool extends ToolBase { .hasArg() .argName("NAME") .desc("Name of the collection.") - .build(); + .get(); private static final Option OUTPUT_OPTION = Option.builder() @@ -102,7 +102,7 @@ public class ExportTool extends ToolBase { .argName("PATH") .desc( "Path to output the exported data, and optionally the file name, defaults to 'collection-name'.") - .build(); + .get(); private static final Option FORMAT_OPTION = Option.builder() @@ -110,10 +110,10 @@ public class ExportTool extends ToolBase { .hasArg() .argName("FORMAT") .desc("Output format for exported docs (json, jsonl or javabin), defaulting to json.") - .build(); + .get(); private static final Option COMPRESS_OPTION = - Option.builder().longOpt("compress").desc("Compress the output. Defaults to false.").build(); + Option.builder().longOpt("compress").desc("Compress the output. Defaults to false.").get(); private static final Option LIMIT_OPTION = Option.builder() @@ -121,7 +121,7 @@ public class ExportTool extends ToolBase { .hasArg() .argName("#") .desc("Maximum number of docs to download. Default is 100, use -1 for all docs.") - .build(); + .get(); private static final Option QUERY_OPTION = Option.builder() @@ -129,7 +129,7 @@ public class ExportTool extends ToolBase { .hasArg() .argName("QUERY") .desc("A custom query, default is '*:*'.") - .build(); + .get(); private static final Option FIELDS_OPTION = Option.builder() @@ -137,7 +137,7 @@ public class ExportTool extends ToolBase { .hasArg() .argName("FIELDA,FIELDB") .desc("Comma separated list of fields to export. By default all fields are fetched.") - .build(); + .get(); public ExportTool(ToolRuntime runtime) { super(runtime); @@ -233,19 +233,12 @@ public static boolean hasExtension(String filename) { } DocsSink getSink() { - DocsSink docSink = null; - switch (format) { - case JAVABIN: - docSink = new JavabinSink(this); - break; - case JSON: - docSink = new JsonSink(this); - break; - case "jsonl": - docSink = new JsonWithLinesSink(this); - break; - } - return docSink; + return switch (format) { + case JAVABIN -> new JavabinSink(this); + case JSON -> new JsonSink(this); + case "jsonl" -> new JsonWithLinesSink(this); + default -> null; + }; } abstract void exportDocs() throws Exception; @@ -290,7 +283,7 @@ public void streamDocListInfo(long numFound, long start, Float maxScore) {} @Override public void runImpl(CommandLine cli) throws Exception { - String url = null; + String url; if (cli.hasOption(CommonCLIOptions.SOLR_URL_OPTION)) { if (!cli.hasOption(COLLECTION_NAME_OPTION)) { throw new IllegalArgumentException( @@ -327,7 +320,7 @@ protected Map processDocument(SolrDocument doc) { if (s.equals("_version_") || s.equals("_roor_")) return; if (field instanceof List) { if (((List) field).size() == 1) { - field = ((List) field).get(0); + field = ((List) field).getFirst(); } } field = constructDateStr(field); @@ -640,7 +633,7 @@ class CoreHandler { this.replica = replica; } - boolean exportDocsFromCore() throws IOException, SolrServerException { + void exportDocsFromCore() throws IOException, SolrServerException { // reference the replica's node URL, not the baseUrl in scope, which could be anywhere try (SolrClient client = CLIUtils.getSolrClient(replica.getBaseUrl(), credentials)) { expectedDocs = getDocCount(replica.getCoreName(), client, query); @@ -665,8 +658,8 @@ boolean exportDocsFromCore() throws IOException, SolrServerException { StreamingJavaBinResponseParser responseParser = new StreamingJavaBinResponseParser(getStreamer(wrapper)); while (true) { - if (failed) return false; - if (docsWritten.get() > limit) return true; + if (failed) return; + if (docsWritten.get() > limit) return; params.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark); request = new QueryRequest(params); request.setResponseParser(responseParser); @@ -683,9 +676,9 @@ boolean exportDocsFromCore() throws IOException, SolrServerException { StrUtils.formatString( "Could not download all docs from core {0}, docs expected: {1}, received: {2}", replica.getCoreName(), expectedDocs, receivedDocs.get())); - return false; + return; } - return true; + return; } cursorMark = nextCursorMark; runtime.print("."); @@ -696,7 +689,7 @@ boolean exportDocsFromCore() throws IOException, SolrServerException { + "/" + replica.getCoreName()); failed = true; - return false; + return; } } } diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index 8745941cff32..670bb0eb6563 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -56,7 +56,7 @@ public class HealthcheckTool extends ToolBase { .argName("COLLECTION") .required() .desc("Name of the collection to check.") - .build(); + .get(); @Override public Options getOptions() { @@ -87,7 +87,6 @@ public void runImpl(CommandLine cli) throws Exception { } try (var cloudSolrClient = CLIUtils.getCloudSolrClient(zkHost)) { echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ..."); - cloudSolrClient.connect(); runCloudTool(cloudSolrClient, cli); } } @@ -194,7 +193,7 @@ protected void runCloudTool(CloudSolrClient cloudSolrClient, CommandLine cli) th ShardHealth shardHealth = new ShardHealth(shardName, replicaList); if (ShardState.healthy != shardHealth.getShardState()) { - collectionIsHealthy = false; // at least one shard is un-healthy + collectionIsHealthy = false; // at least one shard is unhealthy } shardList.add(shardHealth.asMap()); @@ -280,10 +279,8 @@ public boolean equals(Object obj) { @Override public int compareTo(ReplicaHealth other) { if (this == other) return 0; - if (other == null) return 1; int myShardIndex = Integer.parseInt(this.shard.substring("shard".length())); - int otherShardIndex = Integer.parseInt(other.shard.substring("shard".length())); if (myShardIndex == otherShardIndex) { diff --git a/solr/core/src/java/org/apache/solr/cli/LinkConfigTool.java b/solr/core/src/java/org/apache/solr/cli/LinkConfigTool.java index afc14edea832..cb46c6e81933 100644 --- a/solr/core/src/java/org/apache/solr/cli/LinkConfigTool.java +++ b/solr/core/src/java/org/apache/solr/cli/LinkConfigTool.java @@ -35,7 +35,7 @@ public class LinkConfigTool extends ToolBase { .argName("NAME") .required() .desc("Name of the collection to link.") - .build(); + .get(); private static final Option CONF_NAME_OPTION = Option.builder("n") @@ -44,7 +44,7 @@ public class LinkConfigTool extends ToolBase { .argName("NAME") .required() .desc("Configset name in ZooKeeper.") - .build(); + .get(); public LinkConfigTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/PackageTool.java b/solr/core/src/java/org/apache/solr/cli/PackageTool.java index f715a20cd133..aaa3649d1be8 100644 --- a/solr/core/src/java/org/apache/solr/cli/PackageTool.java +++ b/solr/core/src/java/org/apache/solr/cli/PackageTool.java @@ -57,13 +57,13 @@ public class PackageTool extends ToolBase { .argName("COLLECTIONS") .desc( "Specifies that this action should affect plugins for the given collections only, excluding cluster level plugins.") - .build(); + .get(); private static final Option CLUSTER_OPTION = Option.builder() .longOpt("cluster") .desc("Specifies that this action should affect cluster-level plugins only.") - .build(); + .get(); private static final Option PARAM_OPTION = Option.builder() @@ -71,13 +71,13 @@ public class PackageTool extends ToolBase { .hasArgs() .argName("PARAMS") .desc("List of parameters to be used with deploy command.") - .build(); + .get(); private static final Option UPDATE_OPTION = Option.builder() .longOpt("update") .desc("If a deployment is an update over a previous deployment.") - .build(); + .get(); private static final Option COLLECTION_OPTION = Option.builder("c") @@ -85,13 +85,13 @@ public class PackageTool extends ToolBase { .hasArg() .argName("COLLECTION") .desc("The collection to apply the package to, not required.") - .build(); + .get(); private static final Option NO_PROMPT_OPTION = Option.builder("y") .longOpt("no-prompt") .desc("Don't prompt for input; accept all default choices, defaults to false.") - .build(); + .get(); public PackageTool(ToolRuntime runtime) { super(runtime); 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 0cf5e95a9430..82dccf537427 100644 --- a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java +++ b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java @@ -32,7 +32,6 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; @@ -55,7 +54,7 @@ public class PostLogsTool extends ToolBase { .argName("NAME") .required() .desc("Name of the collection.") - .build(); + .get(); private static final Option ROOT_DIR_OPTION = Option.builder() @@ -64,7 +63,7 @@ public class PostLogsTool extends ToolBase { .argName("DIRECTORY") .required() .desc("All files found at or below the root directory will be indexed.") - .build(); + .get(); public PostLogsTool(ToolRuntime runtime) { super(runtime); @@ -86,7 +85,7 @@ public Options getOptions() { @Override public void runImpl(CommandLine cli) throws Exception { - String url = null; + String url; if (cli.hasOption(CommonCLIOptions.SOLR_URL_OPTION)) { url = CLIUtils.normalizeSolrUrl(cli) + "/solr/" + cli.getOptionValue(COLLECTION_NAME_OPTION); @@ -118,7 +117,7 @@ public void runCommand(String baseUrl, String root, String credentials) throws I List files; try (Stream stream = Files.walk(Path.of(root), Integer.MAX_VALUE)) { - files = stream.filter(Files::isRegularFile).collect(Collectors.toList()); + files = stream.filter(Files::isRegularFile).toList(); } for (Path file : files) { @@ -186,24 +185,24 @@ private void sendBatch(SolrClient client, UpdateRequest request, boolean lastReq public static class LogRecordReader { - private BufferedReader bufferedReader; + private final BufferedReader bufferedReader; private String pushedBack = null; private boolean finished = false; private String cause; - private Pattern p = + private final Pattern p = Pattern.compile("^(\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d[\\s|T]\\d\\d:\\d\\d\\:\\d\\d.\\d\\d\\d)"); - private Pattern minute = + private final Pattern minute = Pattern.compile("^(\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d[\\s|T]\\d\\d:\\d\\d)"); - private Pattern tenSecond = + private final Pattern tenSecond = Pattern.compile("^(\\d\\d\\d\\d\\-\\d\\d\\-\\d\\d[\\s|T]\\d\\d:\\d\\d:\\d)"); - public LogRecordReader(BufferedReader bufferedReader) throws IOException { + public LogRecordReader(BufferedReader bufferedReader) { this.bufferedReader = bufferedReader; } public SolrInputDocument readRecord() throws IOException { while (true) { - String line = null; + String line; if (finished) { return null; 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 f4a6dc4085b9..97751490e3f4 100644 --- a/solr/core/src/java/org/apache/solr/cli/PostTool.java +++ b/solr/core/src/java/org/apache/solr/cli/PostTool.java @@ -103,19 +103,19 @@ public class PostTool extends ToolBase { .argName("NAME") .required() .desc("Name of the collection.") - .build(); + .get(); 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.") - .build(); + .get(); private static final Option OPTIMIZE_OPTION = Option.builder("o") .longOpt("optimize") .desc("Issue an optimize at end of posting documents.") - .build(); + .get(); private static final Option MODE_OPTION = Option.builder() @@ -124,7 +124,7 @@ public class PostTool extends ToolBase { .argName("mode") .desc( "Which mode the Post tool is running in, 'files' crawls local directory, 'web' crawls website, 'args' processes input args, and 'stdin' reads a command from standard in. default: files.") - .build(); + .get(); private static final Option RECURSIVE_OPTION = Option.builder("r") @@ -133,7 +133,7 @@ public class PostTool extends ToolBase { .argName("recursive") .type(Integer.class) .desc("For web crawl, how deep to go. default: 1") - .build(); + .get(); private static final Option DELAY_OPTION = Option.builder("d") @@ -143,7 +143,7 @@ public class PostTool extends ToolBase { .type(Integer.class) .desc( "If recursive then delay will be the wait time between posts. default: 10 for web, 0 for files") - .build(); + .get(); private static final Option TYPE_OPTION = Option.builder("t") @@ -151,7 +151,7 @@ public class PostTool extends ToolBase { .hasArg() .argName("content-type") .desc("Specify a specific mimetype to use, such as application/json.") - .build(); + .get(); private static final Option FILE_TYPES_OPTION = Option.builder("ft") @@ -159,7 +159,7 @@ public class PostTool extends ToolBase { .hasArg() .argName("[,,...]") .desc("default: " + DEFAULT_FILE_TYPES) - .build(); + .get(); private static final Option PARAMS_OPTION = Option.builder() @@ -167,21 +167,21 @@ public class PostTool extends ToolBase { .hasArg() .argName("=[&=...]") .desc("Values must be URL-encoded; these pass through to Solr update request.") - .build(); + .get(); private static final Option FORMAT_OPTION = Option.builder() .longOpt("format") .desc( "sends application/json content as Solr commands to /update instead of /update/json/docs.") - .build(); + .get(); private static final Option DRY_RUN_OPTION = Option.builder() .longOpt("dry-run") .desc( "Performs a dry run of the posting process without actually sending documents to Solr. Only works with files mode.") - .build(); + .get(); // Input args int recursive = 0; @@ -326,16 +326,17 @@ public void runImpl(CommandLine cli) throws Exception { */ public void execute(String mode) throws SolrServerException, IOException { final RTimer timer = new RTimer(); - if (PostTool.DATA_MODE_FILES.equals(mode)) { - doFilesMode(); - } else if (DATA_MODE_ARGS.equals(mode)) { - doArgsMode(args); - } else if (PostTool.DATA_MODE_WEB.equals(mode)) { - doWebMode(); - } else if (DATA_MODE_STDIN.equals(mode)) { - doStdinMode(); - } else { - return; + switch (mode) { + case PostTool.DATA_MODE_FILES -> doFilesMode(); + case DATA_MODE_ARGS -> doArgsMode(args); + case PostTool.DATA_MODE_WEB -> doWebMode(); + case DATA_MODE_STDIN -> doStdinMode(); + case null -> { + return; + } + default -> { + return; + } } if (optimize) { @@ -710,7 +711,7 @@ protected int webCrawl(int level, OutputStream out) { */ protected static String computeFullUrl(URL baseUrl, String link) throws MalformedURLException, URISyntaxException { - if (link == null || link.length() == 0) { + if (link == null || link.isEmpty()) { return null; } if (!link.startsWith("http")) { @@ -799,7 +800,7 @@ public static String appendParam(String url, String param) { String[] pa = param.split("&"); StringBuilder urlBuilder = new StringBuilder(url); for (String p : pa) { - if (p.trim().length() == 0) { + if (p.trim().isEmpty()) { continue; } String[] kv = p.split("="); @@ -1285,7 +1286,9 @@ protected List parseRobotsTxt(InputStream is) throws IOException { l = arr[0].trim(); if (l.startsWith(DISALLOW)) { l = l.substring(DISALLOW.length()).trim(); - if (l.length() == 0) continue; + if (l.isEmpty()) { + continue; + } disallows.add(l); } } diff --git a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java index b5b21f6450fd..b0f5959902fb 100644 --- a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java +++ b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java @@ -25,6 +25,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -71,7 +72,7 @@ public class RunExampleTool extends ToolBase { .longOpt("no-prompt") .desc( "Don't prompt for input; accept all defaults when running examples that accept user input.") - .build(); + .get(); private static final Option PROMPT_INPUTS_OPTION = Option.builder() @@ -90,7 +91,7 @@ public class RunExampleTool extends ToolBase { .argName("NAME") .required() .desc("Name of the example to launch, one of: cloud, techproducts, schemaless, films.") - .build(); + .get(); private static final Option SCRIPT_OPTION = Option.builder() @@ -98,7 +99,7 @@ public class RunExampleTool extends ToolBase { .hasArg() .argName("PATH") .desc("Path to the bin/solr script.") - .build(); + .get(); private static final Option SERVER_DIR_OPTION = Option.builder("d") @@ -107,14 +108,14 @@ public class RunExampleTool extends ToolBase { .argName("DIR") .required() .desc("Path to the Solr server directory.") - .build(); + .get(); private static final Option FORCE_OPTION = Option.builder("f") .longOpt("force") .argName("FORCE") .desc("Force option in case Solr is run as root.") - .build(); + .get(); private static final Option EXAMPLE_DIR_OPTION = Option.builder() @@ -123,7 +124,7 @@ public class RunExampleTool extends ToolBase { .argName("DIR") .desc( "Path to the Solr example directory; if not provided, ${serverDir}/../example is expected to exist.") - .build(); + .get(); private static final Option SOLR_HOME_OPTION = Option.builder() @@ -133,7 +134,7 @@ public class RunExampleTool extends ToolBase { .required(false) .desc( "Path to the Solr home directory; if not provided, ${serverDir}/solr is expected to exist.") - .build(); + .get(); private static final Option URL_SCHEME_OPTION = Option.builder() @@ -141,7 +142,7 @@ public class RunExampleTool extends ToolBase { .hasArg() .argName("SCHEME") .desc("Solr URL scheme: http or https, defaults to http if not specified.") - .build(); + .get(); private static final Option PORT_OPTION = Option.builder("p") @@ -149,7 +150,7 @@ public class RunExampleTool extends ToolBase { .hasArg() .argName("PORT") .desc("Specify the port to start the Solr HTTP listener on; default is 8983.") - .build(); + .get(); private static final Option HOST_OPTION = Option.builder() @@ -157,10 +158,10 @@ public class RunExampleTool extends ToolBase { .hasArg() .argName("HOSTNAME") .desc("Specify the hostname for this Solr instance.") - .build(); + .get(); private static final Option USER_MANAGED_OPTION = - Option.builder().longOpt("user-managed").desc("Start Solr in User Managed mode.").build(); + Option.builder().longOpt("user-managed").desc("Start Solr in User Managed mode.").get(); private static final Option MEMORY_OPTION = Option.builder("m") @@ -169,7 +170,7 @@ public class RunExampleTool extends ToolBase { .argName("MEM") .desc( "Sets the min (-Xms) and max (-Xmx) heap size for the JVM, such as: -m 4g results in: -Xms4g -Xmx4g; by default, this script sets the heap size to 512m.") - .build(); + .get(); private static final Option JVM_OPTS_OPTION = Option.builder() @@ -177,7 +178,7 @@ public class RunExampleTool extends ToolBase { .hasArg() .argName("OPTS") .desc("Additional options to be passed to the JVM when starting example Solr server(s).") - .build(); + .get(); protected InputStream userInput; protected Executor executor; @@ -195,7 +196,7 @@ public RunExampleTool(ToolRuntime runtime) { public RunExampleTool(Executor executor, InputStream userInput, ToolRuntime runtime) { super(runtime); - this.executor = (executor != null) ? executor : new DefaultExecutor(); + this.executor = (executor != null) ? executor : new DefaultExecutor.Builder<>().get(); this.userInput = userInput; } @@ -421,85 +422,90 @@ protected void runExample(CommandLine cli, String exampleName) throws Exception SolrCLI.postJsonToSolr( solrClient, "/" + collectionName + "/schema", - "{\n" - + " \"add-field-type\" : {\n" - + " \"name\":\"knn_vector_10\",\n" - + " \"class\":\"solr.DenseVectorField\",\n" - + " \"vectorDimension\":10,\n" - + " \"similarityFunction\":cosine\n" - + " \"knnAlgorithm\":hnsw\n" - + " }\n" - + " }"); + """ + { + "add-field-type" : { + "name":"knn_vector_10", + "class":"solr.DenseVectorField", + "vectorDimension":10, + "similarityFunction":"cosine", + "knnAlgorithm":"hnsw" + } + } + """); echo( "Adding name, genre, directed_by, initial_release_date, and film_vector fields to films schema"); SolrCLI.postJsonToSolr( solrClient, "/" + collectionName + "/schema", - "{\n" - + " \"add-field\" : {\n" - + " \"name\":\"name\",\n" - + " \"type\":\"text_general\",\n" - + " \"multiValued\":false,\n" - + " \"stored\":true\n" - + " },\n" - + " \"add-field\" : {\n" - + " \"name\":\"genre\",\n" - + " \"type\":\"text_general\",\n" - + " \"multiValued\":true,\n" - + " \"stored\":true\n" - + " },\n" - + " \"add-field\" : {\n" - + " \"name\":\"directed_by\",\n" - + " \"type\":\"text_general\",\n" - + " \"multiValued\":true,\n" - + " \"stored\":true\n" - + " },\n" - + " \"add-field\" : {\n" - + " \"name\":\"initial_release_date\",\n" - + " \"type\":\"pdate\",\n" - + " \"stored\":true\n" - + " },\n" - + " \"add-field\" : {\n" - + " \"name\":\"film_vector\",\n" - + " \"type\":\"knn_vector_10\",\n" - + " \"indexed\":true\n" - + " \"stored\":true\n" - + " },\n" - + " \"add-copy-field\" : {\n" - + " \"source\":\"genre\",\n" - + " \"dest\":\"_text_\"\n" - + " },\n" - + " \"add-copy-field\" : {\n" - + " \"source\":\"name\",\n" - + " \"dest\":\"_text_\"\n" - + " },\n" - + " \"add-copy-field\" : {\n" - + " \"source\":\"directed_by\",\n" - + " \"dest\":\"_text_\"\n" - + " }\n" - + " }"); + """ + { + "add-field" : { + "name":"name", + "type":"text_general", + "multiValued":false, + "stored":true + }, + "add-field" : { + "name":"genre", + "type":"text_general", + "multiValued":true, + "stored":true + }, + "add-field" : { + "name":"directed_by", + "type":"text_general", + "multiValued":true, + "stored":true + }, + "add-field" : { + "name":"initial_release_date", + "type":"pdate", + "stored":true + }, + "add-field" : { + "name":"film_vector", + "type":"knn_vector_10", + "indexed":true, + "stored":true + }, + "add-copy-field" : { + "source":"genre", + "dest":"_text_" + }, + "add-copy-field" : { + "source":"name", + "dest":"_text_" + }, + "add-copy-field" : { + "source":"directed_by", + "dest":"_text_" + } + }"""); echo( "Adding paramsets \"algo\" and \"algo_b\" to films configuration for relevancy tuning"); SolrCLI.postJsonToSolr( solrClient, "/" + collectionName + "/config/params", - "{\n" - + " \"set\": {\n" - + " \"algo_a\":{\n" - + " \"defType\":\"dismax\",\n" - + " \"qf\":\"name\"\n" - + " }\n" - + " },\n" - + " \"set\": {\n" - + " \"algo_b\":{\n" - + " \"defType\":\"dismax\",\n" - + " \"qf\":\"name\",\n" - + " \"mm\":\"100%\"\n" - + " }\n" - + " }\n" - + " }\n"); + """ + { + "set": { + "algo_a":{ + "defType":"dismax", + "qf":"name" + } + }, + "set": { + "algo_b":{ + "defType":"dismax", + "qf":"name", + "mm":"100%" + } + } + } + """); Path filmsJsonFile = this.exampleDir.resolve("films").resolve("films.json"); echo("Indexing films example docs from " + filmsJsonFile.toAbsolutePath()); @@ -668,22 +674,26 @@ protected void runCloudExample(CommandLine cli) throws Exception { protected void waitToSeeLiveNodes(String zkHost, int numNodes) { try (CloudSolrClient cloudClient = new CloudSolrClient.Builder(Collections.singletonList(zkHost), Optional.empty()).build()) { - cloudClient.connect(); Set liveNodes = cloudClient.getClusterState().getLiveNodes(); int numLiveNodes = (liveNodes != null) ? liveNodes.size() : 0; - long timeout = System.nanoTime() + TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS); - while (System.nanoTime() < timeout && numLiveNodes < numNodes) { + long timeoutNanos = System.nanoTime() + TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS); + long pollIntervalMs = 2000; + + while (System.nanoTime() < timeoutNanos && numLiveNodes < numNodes) { echo( "\nWaiting up to " + 10 + " seconds to see " + (numNodes - numLiveNodes) + " more nodes join the SolrCloud cluster ..."); + try { - Thread.sleep(2000); + TimeUnit.MILLISECONDS.sleep(pollIntervalMs); } catch (InterruptedException ie) { - Thread.interrupted(); + Thread.currentThread().interrupt(); + break; } + liveNodes = cloudClient.getClusterState().getLiveNodes(); numLiveNodes = (liveNodes != null) ? liveNodes.size() : 0; } @@ -770,13 +780,13 @@ protected Map startSolr( String.format( Locale.ROOT, "%s://%s:%d/solr", urlScheme, (host != null ? host : "localhost"), port); - String credentials = null; // for now we don't need it for example tool. But we should. + String credentials = null; // for now, we don't need it for example tool. But we should. Map nodeStatus = checkPortConflict(solrUrl, credentials, solrHomeDir, port); if (nodeStatus != null) return nodeStatus; // the server they are trying to start is already running - int code = 0; + int code; if (isWindows) { // On Windows, the execution doesn't return, so we have to execute async // and when calling the script, it seems to be inheriting the environment that launched this @@ -810,10 +820,10 @@ protected Map startSolr( executor.execute(startCmd, startEnv, handler); // wait for execution. try { - handler.waitFor(3000); + handler.waitFor(Duration.ofSeconds(3)); } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); // safe to ignore ... - Thread.interrupted(); } if (handler.hasResult() && handler.getExitValue() != 0) { startCmdStr += jvmOptsArg; @@ -1070,7 +1080,7 @@ protected void copyIfNeeded(Path src, Path dest) throws IOException { protected boolean isPortAvailable(int port) { try (Socket s = new Socket("localhost", port)) { - assert s != null; // To allow compilation.. + assert s != null; // To allow compilation. return false; } catch (IOException e) { return true; diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java index 30e8b27c3d69..dfc39bf7cb2d 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java @@ -33,7 +33,7 @@ public class SnapshotCreateTool extends ToolBase { .argName("NAME") .required() .desc("Name of collection to be snapshot.") - .build(); + .get(); private static final Option SNAPSHOT_NAME_OPTION = Option.builder() @@ -42,7 +42,7 @@ public class SnapshotCreateTool extends ToolBase { .argName("NAME") .required() .desc("Name of the snapshot to produce") - .build(); + .get(); public SnapshotCreateTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java index bd9e6aeeb050..00b5c3c01979 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java @@ -33,7 +33,7 @@ public class SnapshotDeleteTool extends ToolBase { .argName("NAME") .required() .desc("Name of collection to manage.") - .build(); + .get(); private static final Option SNAPSHOT_NAME_OPTION = Option.builder() @@ -42,7 +42,7 @@ public class SnapshotDeleteTool extends ToolBase { .argName("NAME") .required() .desc("Name of the snapshot to delete") - .build(); + .get(); public SnapshotDeleteTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java index 6e6fef8f0e88..477ad265e7d5 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java @@ -43,7 +43,7 @@ public class SnapshotDescribeTool extends ToolBase { .argName("NAME") .required() .desc("Name of collection to be snapshot.") - .build(); + .get(); private static final Option SNAPSHOT_NAME_OPTION = Option.builder() @@ -52,7 +52,7 @@ public class SnapshotDescribeTool extends ToolBase { .argName("NAME") .required() .desc("Name of the snapshot to describe") - .build(); + .get(); public SnapshotDescribeTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java index ea47d1b2a319..d71b8df8b1a2 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java @@ -16,7 +16,6 @@ */ package org.apache.solr.cli; -import java.util.Optional; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -34,7 +33,7 @@ public class SnapshotExportTool extends ToolBase { .argName("NAME") .required() .desc("Name of collection to be snapshot.") - .build(); + .get(); private static final Option SNAPSHOT_NAME_OPTION = Option.builder() @@ -43,7 +42,7 @@ public class SnapshotExportTool extends ToolBase { .argName("NAME") .required() .desc("Name of the snapshot to be exported.") - .build(); + .get(); private static final Option DEST_DIR_OPTION = Option.builder() @@ -52,7 +51,7 @@ public class SnapshotExportTool extends ToolBase { .argName("DIR") .required() .desc("Path of a temporary directory on local filesystem during snapshot export command.") - .build(); + .get(); private static final Option BACKUP_REPO_NAME_OPTION = Option.builder() @@ -61,7 +60,7 @@ public class SnapshotExportTool extends ToolBase { .argName("DIR") .desc( "Specifies name of the backup repository to be used during snapshot export preparation.") - .build(); + .get(); private static final Option ASYNC_ID_OPTION = Option.builder() @@ -70,7 +69,7 @@ public class SnapshotExportTool extends ToolBase { .argName("ID") .desc( "Specifies the async request identifier to be used during snapshot export preparation.") - .build(); + .get(); public SnapshotExportTool(ToolRuntime runtime) { super(runtime); @@ -98,8 +97,8 @@ public void runImpl(CommandLine cli) throws Exception { String snapshotName = cli.getOptionValue(SNAPSHOT_NAME_OPTION); String collectionName = cli.getOptionValue(COLLECTION_NAME_OPTION); String destDir = cli.getOptionValue(DEST_DIR_OPTION); - Optional backupRepo = Optional.ofNullable(cli.getOptionValue(BACKUP_REPO_NAME_OPTION)); - Optional asyncReqId = Optional.ofNullable(cli.getOptionValue(ASYNC_ID_OPTION)); + String backupRepo = cli.getOptionValue(BACKUP_REPO_NAME_OPTION); + String asyncReqId = cli.getOptionValue(ASYNC_ID_OPTION); try (var solrClient = CLIUtils.getSolrClient(cli)) { exportSnapshot(solrClient, collectionName, snapshotName, destDir, backupRepo, asyncReqId); @@ -111,19 +110,19 @@ public void exportSnapshot( String collectionName, String snapshotName, String destPath, - Optional backupRepo, - Optional asyncReqId) { + String backupRepo, + String asyncReqId) { try { CollectionAdminRequest.Backup backup = new CollectionAdminRequest.Backup(collectionName, snapshotName); backup.setCommitName(snapshotName); backup.setIndexBackupStrategy(CollectionAdminParams.COPY_FILES_STRATEGY); backup.setLocation(destPath); - if (backupRepo.isPresent()) { - backup.setRepositoryName(backupRepo.get()); + if (backupRepo != null) { + backup.setRepositoryName(backupRepo); } // if asyncId is null, processAsync will block and throw an Exception with any error - backup.processAsync(asyncReqId.orElse(null), solrClient); + backup.processAsync(asyncReqId, solrClient); } catch (Exception e) { throw new IllegalStateException( "Failed to backup collection meta-data for collection " diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java index be40d0361f63..4501fddc8397 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java @@ -35,7 +35,7 @@ public class SnapshotListTool extends ToolBase { .argName("NAME") .required() .desc("Name of collection to list snapshots for.") - .build(); + .get(); public SnapshotListTool(ToolRuntime runtime) { super(runtime); 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 e6a16b3a6e9a..2db5bee92638 100755 --- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java +++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java @@ -17,6 +17,7 @@ package org.apache.solr.cli; import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; import java.lang.invoke.MethodHandles; import java.net.URI; import java.net.URL; @@ -34,14 +35,16 @@ import java.util.zip.ZipInputStream; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.help.HelpFormatter; +import org.apache.commons.cli.help.TextHelpAppendable; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SuppressForbidden; import org.apache.solr.util.configuration.SSLConfigurationsFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +59,7 @@ public static void main(String[] args) throws Exception { ToolRuntime runtime = new DefaultToolRuntime(); final boolean hasNoCommand = - args == null || args.length == 0 || args[0] == null || args[0].trim().length() == 0; + args == null || args.length == 0 || args[0] == null || args[0].trim().isEmpty(); final boolean isHelpCommand = !hasNoCommand && Arrays.asList("-h", "--help").contains(args[0]); if (hasNoCommand || isHelpCommand) { @@ -118,7 +121,7 @@ public static Tool findTool(String[] args, ToolRuntime runtime) throws Exception return newTool(toolType, runtime); } - public static CommandLine parseCmdLine(Tool tool, String[] args) { + public static CommandLine parseCmdLine(Tool tool, String[] args) throws IOException { // the parser doesn't like -D props List toolArgList = new ArrayList<>(); List dashDList = new ArrayList<>(); @@ -249,7 +252,7 @@ private static void deprecatedHandlerStdErr(Option o) { } /** Parses the command-line arguments passed by the user. */ - public static CommandLine processCommandLineArgs(Tool tool, String[] args) { + public static CommandLine processCommandLineArgs(Tool tool, String[] args) throws IOException { Options options = tool.getOptions(); ToolRuntime runtime = tool.getRuntime(); @@ -258,7 +261,7 @@ public static CommandLine processCommandLineArgs(Tool tool, String[] args) { cli = DefaultParser.builder() .setDeprecatedHandler(SolrCLI::deprecatedHandlerStdErr) - .build() + .get() .parse(options, args); } catch (ParseException exp) { // Check if we passed in a help argument with a non parsing set of arguments. @@ -290,7 +293,7 @@ public static CommandLine processCommandLineArgs(Tool tool, String[] args) { } /** Prints tool help for a given tool */ - public static void printToolHelp(Tool tool) { + public static void printToolHelp(Tool tool) throws IOException { HelpFormatter formatter = getFormatter(); Options nonDeprecatedOptions = new Options(); @@ -308,10 +311,38 @@ public static void printToolHelp(Tool tool) { autoGenerateUsage); } + @SuppressForbidden(reason = "System.out for formatting") public static HelpFormatter getFormatter() { - HelpFormatter formatter = HelpFormatter.builder().get(); - formatter.setWidth(120); - return formatter; + TextHelpAppendable helpAppendable = + new TextHelpAppendable(System.out) { + @Override + public void appendTable(org.apache.commons.cli.help.TableDefinition table) + throws IOException { + if (table == null) { + return; + } + // Create a new TableDefinition with empty headers to suppress the header row + java.util.List emptyHeaders = + java.util.Collections.nCopies(table.headers().size(), ""); + org.apache.commons.cli.help.TableDefinition noHeaderTable = + org.apache.commons.cli.help.TableDefinition.from( + table.caption(), table.columnTextStyles(), emptyHeaders, table.rows()); + super.appendTable(noHeaderTable); + } + + @Override + public void appendParagraph(CharSequence paragraph) throws IOException { + String text = paragraph.toString().stripTrailing(); + if (!text.isEmpty()) { + super.append(text); + super.append("\n"); + } + } + }; + helpAppendable.setMaxWidth(120); + helpAppendable.setIndent(0); + helpAppendable.setLeftPad(0); + return HelpFormatter.builder().setHelpAppendable(helpAppendable).setShowSince(false).get(); } /** Scans Jar files on the classpath for Tool implementations to activate. */ diff --git a/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java b/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java index 5dfdad3a5f89..67146509da50 100644 --- a/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java +++ b/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java @@ -117,15 +117,15 @@ public Collection scanSolrPidFiles() throws IOException { try (Stream pidFiles = Files.list(pidDir) .filter(p -> pidFilePattern.matcher(p.getFileName().toString()).matches())) { - for (Path p : pidFiles.collect(Collectors.toList())) { + for (Path p : pidFiles.toList()) { Optional process; if (p.toString().endsWith(".port")) { // On Windows, the file is a 'PORT' file containing the port number. - Integer port = Integer.valueOf(Files.readAllLines(p).get(0)); + Integer port = Integer.valueOf(Files.readAllLines(p).getFirst()); process = processForPort(port); } else { // On Linux, the file is a 'PID' file containing the process ID. - Long pid = Long.valueOf(Files.readAllLines(p).get(0)); + Long pid = Long.valueOf(Files.readAllLines(p).getFirst()); process = getProcessForPid(pid); } if (process.isPresent()) { @@ -159,7 +159,7 @@ private boolean isProcessSsl(ProcessHandle ph) { } /** - * Gets the command line of a process as a string. For Windows we need to fetch command lines + * Gets the command line of a process as a string. For Windows, we need to fetch command lines * using a PowerShell command. * * @param ph the process handle @@ -238,28 +238,7 @@ private static List arguments(ProcessHandle ph) { } /** Represents a running Solr process */ - public static class SolrProcess { - private final long pid; - private final int port; - private final boolean isHttps; - - public SolrProcess(long pid, int port, boolean isHttps) { - this.pid = pid; - this.port = port; - this.isHttps = isHttps; - } - - public long getPid() { - return pid; - } - - public int getPort() { - return port; - } - - public boolean isHttps() { - return isHttps; - } + public record SolrProcess(long pid, int port, boolean isHttps) { public String getLocalUrl() { return String.format(Locale.ROOT, "%s://localhost:%s/solr", isHttps ? "https" : "http", port); diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java b/solr/core/src/java/org/apache/solr/cli/StatusTool.java index 9fee5940a177..1783e1bef1d3 100644 --- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java +++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java @@ -54,7 +54,7 @@ public class StatusTool extends ToolBase { .type(Integer.class) .deprecated() // Will make it a stealth option, not printed or complained about .desc("Wait up to the specified number of seconds to see Solr running.") - .build(); + .get(); public static final Option PORT_OPTION = Option.builder("p") @@ -63,14 +63,14 @@ public class StatusTool extends ToolBase { .argName("PORT") .type(Integer.class) .desc("Port on localhost to check status for") - .build(); + .get(); public static final Option SHORT_OPTION = Option.builder() .longOpt("short") .argName("SHORT") .desc("Short format. Prints one URL per line for running instances") - .build(); + .get(); private final SolrProcessManager processMgr; @@ -172,8 +172,8 @@ private void printProcessStatus(SolrProcess process, CommandLine cli) throws Exc String.format( Locale.ROOT, "\nSolr process %s running on port %s", - process.getPid(), - process.getPort())); + process.pid(), + process.port())); printStatusFromRunningSolr(pidUrl, cli); } } @@ -291,8 +291,7 @@ public Map waitToSeeSolrUp( public Map getStatus(String solrUrl, String credentials) throws Exception { try (var solrClient = CLIUtils.getSolrClient(solrUrl, credentials)) { - Map status = reportStatus(solrClient); - return status; + return reportStatus(solrClient); } } diff --git a/solr/core/src/java/org/apache/solr/cli/StreamTool.java b/solr/core/src/java/org/apache/solr/cli/StreamTool.java index 651eee6cc7b7..61d95c79d418 100644 --- a/solr/core/src/java/org/apache/solr/cli/StreamTool.java +++ b/solr/core/src/java/org/apache/solr/cli/StreamTool.java @@ -72,8 +72,10 @@ public String getName() { @Override public String getUsage() { // Specify that the last argument is the streaming expression - return "bin/solr stream [--array-delimiter ] [-c ] [--delimiter ] [--execution ] [--fields\n" - + " ] [-h] [--header] [-s ] [-u ] [-v] [-z ] \n"; + return """ + bin/solr stream [--array-delimiter ] [-c ] [--delimiter ] [--execution ] [--fields + ] [-h] [--header] [-s ] [-u ] [-v] [-z ] + """; } private static final Option EXECUTION_OPTION = @@ -83,7 +85,7 @@ public String getUsage() { .argName("ENVIRONMENT") .desc( "Execution environment is either 'local' (i.e CLI process) or via a 'remote' Solr server. Default environment is 'remote'.") - .build(); + .get(); private static final Option COLLECTION_OPTION = Option.builder("c") @@ -92,7 +94,7 @@ public String getUsage() { .hasArg() .desc( "Name of the specific collection to execute expression on if the execution is set to 'remote'. Required for 'remote' execution environment.") - .build(); + .get(); private static final Option FIELDS_OPTION = Option.builder() @@ -101,10 +103,10 @@ public String getUsage() { .hasArg() .desc( "The fields in the tuples to output. Defaults to fields in the first tuple of result set.") - .build(); + .get(); private static final Option HEADER_OPTION = - Option.builder().longOpt("header").desc("Specify to include a header line.").build(); + Option.builder().longOpt("header").desc("Specify to include a header line.").get(); private static final Option DELIMITER_OPTION = Option.builder() @@ -112,14 +114,14 @@ public String getUsage() { .argName("CHARACTER") .hasArg() .desc("The output delimiter. Default to using three spaces.") - .build(); + .get(); private static final Option ARRAY_DELIMITER_OPTION = Option.builder() .longOpt("array-delimiter") .argName("CHARACTER") .hasArg() .desc("The delimiter multi-valued fields. Default to using a pipe (|) delimiter.") - .build(); + .get(); @Override public Options getOptions() { @@ -268,6 +270,7 @@ private PushBackStream doLocalMode(CommandLine cli, String expr) throws Exceptio Lang.register(streamFactory); + assert streamExpression != null; stream = streamFactory.constructStream(streamExpression); pushBackStream = new PushBackStream(stream); @@ -467,7 +470,7 @@ static String[] getHeadersFromFirstTuple(Tuple tuple) { static String listToString(List values, String internalDelim) { StringBuilder buf = new StringBuilder(); for (Object value : values) { - if (buf.length() > 0) { + if (!buf.isEmpty()) { buf.append(internalDelim); } @@ -504,7 +507,7 @@ static String readExpression(LineNumberReader bufferedReader, String[] args) thr // Substitute parameters - if (line.length() > 0) { + if (!line.isEmpty()) { for (int i = 1; i < args.length; i++) { String arg = args[i]; line = line.replace("$" + i, arg); diff --git a/solr/core/src/java/org/apache/solr/cli/ZkCpTool.java b/solr/core/src/java/org/apache/solr/cli/ZkCpTool.java index 09ebe88cf940..4bf9a2663156 100644 --- a/solr/core/src/java/org/apache/solr/cli/ZkCpTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ZkCpTool.java @@ -48,7 +48,7 @@ public class ZkCpTool extends ToolBase { .hasArg() .argName("DIR") .desc("Required to look up configuration for compressing state.json.") - .build(); + .get(); public ZkCpTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/ZkMkrootTool.java b/solr/core/src/java/org/apache/solr/cli/ZkMkrootTool.java index dc0a4076e599..2a644f2a1364 100644 --- a/solr/core/src/java/org/apache/solr/cli/ZkMkrootTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ZkMkrootTool.java @@ -35,7 +35,7 @@ public class ZkMkrootTool extends ToolBase { .longOpt("fail-on-exists") .hasArg() .desc("Raise an error if the root exists. Defaults to false.") - .build(); + .get(); public ZkMkrootTool(ToolRuntime runtime) { super(runtime); diff --git a/solr/core/src/java/org/apache/solr/cli/ZkRmTool.java b/solr/core/src/java/org/apache/solr/cli/ZkRmTool.java index 64155413e0a6..92e6b4daaa4e 100644 --- a/solr/core/src/java/org/apache/solr/cli/ZkRmTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ZkRmTool.java @@ -67,7 +67,7 @@ public void runImpl(CommandLine cli) throws Exception { } echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ..."); try (SolrZkClient zkClient = CLIUtils.getSolrZkClient(cli, zkHost)) { - if (!recursive && zkClient.getChildren(znode, null).size() != 0) { + if (!recursive && !zkClient.getChildren(znode, null).isEmpty()) { throw new SolrServerException( "ZooKeeper node " + znode + " has children and recursive has NOT been specified."); } diff --git a/solr/core/src/java/org/apache/solr/cli/ZkToolHelp.java b/solr/core/src/java/org/apache/solr/cli/ZkToolHelp.java index 93c1672de48c..84f0040ae23e 100644 --- a/solr/core/src/java/org/apache/solr/cli/ZkToolHelp.java +++ b/solr/core/src/java/org/apache/solr/cli/ZkToolHelp.java @@ -29,13 +29,13 @@ public class ZkToolHelp extends ToolBase { Option.builder() .longOpt("print-zk-subcommand-usage") .desc("Reminds user to prepend zk to invoke the command.") - .build(); + .get(); private static final Option PRINT_LONG_ZK_USAGE_OPTION = Option.builder() .longOpt("print-long-zk-usage") .desc("Invokes the detailed help for zk commands.") - .build(); + .get(); public ZkToolHelp(ToolRuntime runtime) { super(runtime); 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 c1ebe13af0e5..6893e51dc227 100644 --- a/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java +++ b/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java @@ -102,7 +102,7 @@ private void assertFindInJson(String json, String find) { } @Test - public void testSolrUrlParsing() throws Exception { + public void testSolrUrlParsing() { assertEquals( "https://test-host.solr:8983/solr", ApiTool.getSolrUrlFromUri(URI.create("https://test-host.solr:8983/solr"))); diff --git a/solr/core/src/test/org/apache/solr/cli/CLITestHelper.java b/solr/core/src/test/org/apache/solr/cli/CLITestHelper.java index f1701ef9c2a7..9c2cd4afb2f7 100644 --- a/solr/core/src/test/org/apache/solr/cli/CLITestHelper.java +++ b/solr/core/src/test/org/apache/solr/cli/CLITestHelper.java @@ -108,6 +108,7 @@ public void clearOutput() { } printer.flush(); + assert writer != null; writer.getBuffer().setLength(0); } @@ -117,6 +118,7 @@ public String getOutput() { } printer.flush(); + assert writer != null; return writer.toString(); } @@ -125,6 +127,7 @@ public Reader getReader() { fail("TestingRuntime was created without capturing output"); } + assert writer != null; return new StringReader(writer.toString()); } 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 268c532f71b9..dc9e7226dfb2 100644 --- a/solr/core/src/test/org/apache/solr/cli/PostToolTest.java +++ b/solr/core/src/test/org/apache/solr/cli/PostToolTest.java @@ -76,9 +76,10 @@ public void testBasicRun() throws Exception { Path jsonDoc = Files.createTempFile("temp", ".json"); - BufferedWriter fw = Files.newBufferedWriter(jsonDoc, StandardCharsets.UTF_8); - Utils.writeJson(Map.of("id", "1", "title_s", "mytitle"), fw, true); - fw.flush(); + try (BufferedWriter fw = Files.newBufferedWriter(jsonDoc, StandardCharsets.UTF_8)) { + Utils.writeJson(Map.of("id", "1", "title_s", "mytitle"), fw, true); + fw.flush(); + } String[] args = { "post", @@ -120,9 +121,10 @@ public void testRunWithCollectionParam() throws Exception { Path jsonDoc = Files.createTempFile("temp", ".json"); - BufferedWriter fw = Files.newBufferedWriter(jsonDoc, StandardCharsets.UTF_8); - Utils.writeJson(Map.of("id", "1", "title_s", "mytitle"), fw, true); - fw.flush(); + try (BufferedWriter fw = Files.newBufferedWriter(jsonDoc, StandardCharsets.UTF_8)) { + Utils.writeJson(Map.of("id", "1", "title_s", "mytitle"), fw, true); + fw.flush(); + } String[] args = { "post", "-c", collection, "--credentials", SecurityJson.USER_PASS, jsonDoc.toString(), @@ -403,11 +405,13 @@ public MockPageFetcher(ToolRuntime runtime) throws IOException, URISyntaxExcepti // Simulate a robots.txt file with comments and a few disallows String sb = - "# Comments appear after the \"#\" symbol at the start of a line, or after a directive\n" - + "User-agent: * # match all bots\n" - + "Disallow: # This is void\n" - + "Disallow: /disallow # Disallow this path\n" - + "Disallow: /nonexistentpath # Disallow this path\n"; + """ + # Comments appear after the "#" symbol at the start of a line, or after a directive + User-agent: * # match all bots + Disallow: # This is void + Disallow: /disallow # Disallow this path + Disallow: /nonexistentpath # Disallow this path + """; this.robotsCache.put( "[ff01::114]", super.parseRobotsTxt(new ByteArrayInputStream(sb.getBytes(StandardCharsets.UTF_8)))); diff --git a/solr/core/src/test/org/apache/solr/cli/SolrCLIZkToolsTest.java b/solr/core/src/test/org/apache/solr/cli/SolrCLIZkToolsTest.java index 9124fd2b16ac..bd8f8a8a151f 100644 --- a/solr/core/src/test/org/apache/solr/cli/SolrCLIZkToolsTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SolrCLIZkToolsTest.java @@ -576,7 +576,7 @@ public void testRm() throws Exception { assertNotEquals("Should fail when trying to remove /.", 0, res); } - // Check that all children of fileRoot are children of zkRoot and vice-versa + // Check that all children of fileRoot are children of zkRoot and vice versa private void verifyZkLocalPathsMatch(Path fileRoot, String zkRoot) throws IOException, KeeperException, InterruptedException { verifyAllFilesAreZNodes(fileRoot, zkRoot); diff --git a/solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java b/solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java index e1bb9d51cd01..2102e1e3d75d 100644 --- a/solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java @@ -79,13 +79,15 @@ private static int findAvailablePort() throws IOException { } } + @SuppressWarnings("SystemGetProperty") private static Pair createProcess(int port, boolean https) throws IOException { // Get the path to the java executable from the current JVM + + String pathSeparator = System.getProperty("path.separator"); String classPath = - Arrays.stream( - System.getProperty("java.class.path").split(System.getProperty("path.separator"))) + Arrays.stream(System.getProperty("java.class.path").split(pathSeparator)) .filter(p -> p.contains("solr") && p.contains("core") && p.contains("build")) - .collect(Collectors.joining(System.getProperty("path.separator"))); + .collect(Collectors.joining(pathSeparator)); ProcessBuilder processBuilder = new ProcessBuilder( @@ -115,7 +117,7 @@ public void testGetLocalUrl() { .forEach( p -> assertEquals( - (p.isHttps() ? "https" : "http") + "://localhost:" + p.getPort() + "/solr", + (p.isHttps() ? "https" : "http") + "://localhost:" + p.port() + "/solr", p.getLocalUrl())); } @@ -134,22 +136,19 @@ public void testIsRunningWithPid() { public void testProcessForPort() { assertEquals( processHttp.getKey().intValue(), - (solrProcessManager.processForPort(processHttp.getKey()).orElseThrow().getPort())); + (solrProcessManager.processForPort(processHttp.getKey()).orElseThrow().port())); assertEquals( processHttps.getKey().intValue(), - (solrProcessManager.processForPort(processHttps.getKey()).orElseThrow().getPort())); + (solrProcessManager.processForPort(processHttps.getKey()).orElseThrow().port())); } public void testGetProcessForPid() { assertEquals( processHttp.getValue().pid(), - (solrProcessManager.getProcessForPid(processHttp.getValue().pid()).orElseThrow().getPid())); + (solrProcessManager.getProcessForPid(processHttp.getValue().pid()).orElseThrow().pid())); assertEquals( processHttps.getValue().pid(), - (solrProcessManager - .getProcessForPid(processHttps.getValue().pid()) - .orElseThrow() - .getPid())); + (solrProcessManager.getProcessForPid(processHttps.getValue().pid()).orElseThrow().pid())); } public void testScanSolrPidFiles() throws IOException { @@ -164,14 +163,14 @@ public void testGetAllRunning() { public void testSolrProcessMethods() { SolrProcess http = solrProcessManager.processForPort(processHttp.getKey()).orElseThrow(); - assertEquals(processHttp.getValue().pid(), http.getPid()); - assertEquals(processHttp.getKey().intValue(), http.getPort()); + assertEquals(processHttp.getValue().pid(), http.pid()); + assertEquals(processHttp.getKey().intValue(), http.port()); assertFalse(http.isHttps()); assertEquals("http://localhost:" + processHttp.getKey() + "/solr", http.getLocalUrl()); SolrProcess https = solrProcessManager.processForPort(processHttps.getKey()).orElseThrow(); - assertEquals(processHttps.getValue().pid(), https.getPid()); - assertEquals(processHttps.getKey().intValue(), https.getPort()); + assertEquals(processHttps.getValue().pid(), https.pid()); + assertEquals(processHttps.getKey().intValue(), https.port()); assertTrue(https.isHttps()); assertEquals("https://localhost:" + processHttps.getKey() + "/solr", https.getLocalUrl()); } @@ -187,7 +186,7 @@ public void testParseWindowsPidToCommandLineJson() throws JsonProcessingExceptio /** * This class is started as new java process by {@link SolrProcessManagerTest#createProcess}, and - * it listens to a HTTP(s) port to simulate a real Solr process. + * it listens to an HTTP(s) port to simulate a real Solr process. */ @SuppressWarnings("NewClassNamingConvention") public static class MockSolrProcess { diff --git a/solr/core/src/test/org/apache/solr/cli/StreamToolTest.java b/solr/core/src/test/org/apache/solr/cli/StreamToolTest.java index 497b005ad714..5d60662189ec 100644 --- a/solr/core/src/test/org/apache/solr/cli/StreamToolTest.java +++ b/solr/core/src/test/org/apache/solr/cli/StreamToolTest.java @@ -19,6 +19,7 @@ import java.io.BufferedWriter; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.LineNumberReader; import java.io.PrintWriter; import java.io.StringReader; @@ -69,7 +70,7 @@ public void testGetHeaderFromFirstTuple() { } @Test - public void testGetOutputFields() { + public void testGetOutputFields() throws IOException { String[] args = new String[] { "--fields", "field9, field2, field3, field4", diff --git a/solr/core/src/test/org/apache/solr/cli/TestExportTool.java b/solr/core/src/test/org/apache/solr/cli/TestExportTool.java index 8a2d605640ba..ef6b20853d74 100644 --- a/solr/core/src/test/org/apache/solr/cli/TestExportTool.java +++ b/solr/core/src/test/org/apache/solr/cli/TestExportTool.java @@ -292,7 +292,7 @@ public void testWithBasicAuth() throws Exception { private void assertJavabinDocsCount(ExportTool.Info info, int expected) throws IOException { assertTrue( - "" + info.docsWritten.get() + " expected " + expected, info.docsWritten.get() >= expected); + info.docsWritten.get() + " expected " + expected, info.docsWritten.get() >= expected); try (FileInputStream fis = new FileInputStream(info.out)) { int[] count = new int[] {0}; FastInputStream in = FastInputStream.wrap(fis); @@ -309,14 +309,14 @@ private void assertJavabinDocsCount(ExportTool.Info info, int expected) throws I private void assertJsonDocsCount(ExportTool.Info info, int expected) { assertTrue( - "" + info.docsWritten.get() + " expected " + expected, info.docsWritten.get() >= expected); + info.docsWritten.get() + " expected " + expected, info.docsWritten.get() >= expected); } private void assertJsonDocsCount( ExportTool.Info info, int expected, Predicate> predicate) throws IOException { assertTrue( - "" + info.docsWritten.get() + " expected " + expected, info.docsWritten.get() >= expected); + info.docsWritten.get() + " expected " + expected, info.docsWritten.get() >= expected); JsonRecordReader jsonReader; Reader rdr; @@ -342,7 +342,7 @@ private void assertJsonLinesDocsCount( ExportTool.Info info, int expected, Predicate> predicate) throws IOException { assertTrue( - "" + info.docsWritten.get() + " expected " + expected, info.docsWritten.get() >= expected); + info.docsWritten.get() + " expected " + expected, info.docsWritten.get() >= expected); JsonRecordReader jsonReader; Reader rdr; diff --git a/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java b/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java index 298bbfb0644e..4e6c0fecf4cd 100644 --- a/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java +++ b/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.TimeUnit; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteResultHandler; @@ -71,6 +72,7 @@ public static void beforeClass() { * Overrides the call to exec bin/solr to start Solr nodes to start them using the Solr * test-framework instead of the script, since the script depends on a full build. */ + @SuppressWarnings("deprecation") // Using DefaultExecutor for test mocking purposes private static class RunExampleExecutor extends DefaultExecutor implements Closeable { private final List commandsExecuted = new ArrayList<>(); @@ -212,20 +214,18 @@ protected int startStandaloneSolr(String[] args) { standaloneSolr = new JettySolrRunner(solrHomeDir.toString(), port); Thread bg = - new Thread() { - @Override - public void run() { - try { - standaloneSolr.start(); - } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new RuntimeException(e); + new Thread( + () -> { + try { + standaloneSolr.start(); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } else { + throw new RuntimeException(e); + } } - } - } - }; + }); bg.start(); return 0; @@ -245,7 +245,7 @@ protected String getArg(String arg, String[] args) { "Missing required arg " + arg + " needed to execute command: " - + commandsExecuted.get(commandsExecuted.size() - 1)); + + commandsExecuted.getLast()); } protected boolean hasFlag(String flag, String[] args) { @@ -323,7 +323,7 @@ protected void testExample(String exampleName) throws Exception { for (int pass = 0; pass < 2; pass++) { // need a port to start the example server on - int bindPort = -1; + int bindPort; try (ServerSocket socket = new ServerSocket(0)) { bindPort = socket.getLocalPort(); } @@ -386,11 +386,7 @@ protected void testExample(String exampleName) throws Exception { // brief wait in case of timing issue in getting the new docs committed log.warn( "Going to wait for 1 second before re-trying query for techproduct example docs ..."); - try { - Thread.sleep(1000); - } catch (InterruptedException ignore) { - Thread.interrupted(); - } + TimeUnit.MILLISECONDS.sleep(1000); numFound = solrClient.query(query).getResults().getNumFound(); } assertEquals( @@ -433,7 +429,7 @@ public void testInteractiveSolrCloudExample() throws Exception { "--example-dir", solrExampleDir.toString() }; - int bindPort = -1; + int bindPort; try (ServerSocket socket = new ServerSocket(0)) { bindPort = socket.getLocalPort(); } @@ -481,7 +477,7 @@ public void testInteractiveSolrCloudExample() throws Exception { // index some docs - to verify all is good for both shards try (CloudSolrClient cloudClient = - new RandomizingCloudSolrClientBuilder( + new RandomizingCloudHttp2SolrClientBuilder( Collections.singletonList(executor.solrCloudCluster.getZkServer().getZkAddress()), Optional.empty()) .withDefaultCollection(collectionName) @@ -645,7 +641,7 @@ public void testFailExecuteScript() throws Exception { Path solrServerDir = solrHomeDir.getParent(); // need a port to start the example server on - int bindPort = -1; + int bindPort; try (ServerSocket socket = new ServerSocket(0)) { bindPort = socket.getLocalPort(); } diff --git a/solr/core/src/test/org/apache/solr/cli/ZkSubcommandsTest.java b/solr/core/src/test/org/apache/solr/cli/ZkSubcommandsTest.java index 14c286862b02..1d7d7f9a61c7 100644 --- a/solr/core/src/test/org/apache/solr/cli/ZkSubcommandsTest.java +++ b/solr/core/src/test/org/apache/solr/cli/ZkSubcommandsTest.java @@ -18,7 +18,6 @@ import java.io.BufferedWriter; import java.io.IOException; -import java.io.PrintStream; import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -34,8 +33,8 @@ import org.apache.solr.cloud.ZkTestServer; import org.apache.solr.common.cloud.ClusterProperties; import org.apache.solr.common.cloud.DigestZkACLProvider; +import org.apache.solr.common.cloud.DigestZkCredentialsProvider; import org.apache.solr.common.cloud.SolrZkClient; -import org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider; import org.apache.solr.common.cloud.VMParamsZkCredentialsInjector; import org.apache.solr.common.cloud.ZkNodeProps; import org.apache.solr.common.cloud.ZkStateReader; @@ -56,8 +55,6 @@ public class ZkSubcommandsTest extends SolrTestCaseJ4 { private SolrZkClient zkClient; - private PrintStream originalSystemOut; - protected static final Path SOLR_HOME = SolrTestCaseJ4.TEST_HOME(); @BeforeClass @@ -71,18 +68,18 @@ public void setUp() throws Exception { if (log.isInfoEnabled()) { log.info("####SETUP_START {}", getTestName()); } + // originalSystemOut = System.out; String solrHome = createTempDir().toString(); System.setProperty("solr.home", solrHome); - originalSystemOut = System.out; + Path zkDir = createTempDir().resolve("zookeeper/server1/data"); Path zkDir = createTempDir().resolve("zookeeper/server1/data"); log.info("ZooKeeper dataDir:{}", zkDir); zkServer = new ZkTestServer(zkDir); zkServer.run(); System.setProperty("zkHost", zkServer.getZkAddress()); - // zkClient.close(); zkClient = new SolrZkClient.Builder() @@ -162,8 +159,8 @@ public void testPutCompressed() throws Exception { dataBytes = data.getBytes(StandardCharsets.UTF_8); expected = zLibCompressor.compressBytes(dataBytes); - byte[] fromLoca = new ZLibCompressor().compressBytes(Files.readAllBytes(localFile)); - assertArrayEquals("Should get back what we put in ZK", fromLoca, expected); + byte[] fromLocal = new ZLibCompressor().compressBytes(Files.readAllBytes(localFile)); + assertArrayEquals("Should get back what we put in ZK", fromLocal, expected); args = new String[] {"cp", "-z", zkServer.getZkAddress(), localFile.toString(), "zk:/state.json"}; @@ -258,7 +255,7 @@ public void testPutFileCompressed() throws Exception { assertArrayEquals( "Should get back an uncompressed version what we put in ZK", fileBytes, fromZk); - // Lets do it again + // Let's do it again assertEquals(0, CLITestHelper.runTool(args, ZkCpTool.class)); locFile = SOLR_HOME.resolve("solr-stress-new.xml"); @@ -552,21 +549,18 @@ public void testSetClusterProperty() throws Exception { @Test public void testUpdateAcls() throws Exception { + System.setProperty( + SolrZkClient.ZK_CRED_PROVIDER_CLASS_NAME_VM_PARAM_NAME, + DigestZkCredentialsProvider.class.getName()); System.setProperty( SolrZkClient.ZK_ACL_PROVIDER_CLASS_NAME_VM_PARAM_NAME, DigestZkACLProvider.class.getName()); + System.setProperty( + SolrZkClient.ZK_CREDENTIALS_INJECTOR_CLASS_NAME_VM_PARAM_NAME, + VMParamsZkCredentialsInjector.class.getName()); System.setProperty( VMParamsZkCredentialsInjector.DEFAULT_DIGEST_READONLY_USERNAME_VM_PARAM_NAME, "user"); System.setProperty( VMParamsZkCredentialsInjector.DEFAULT_DIGEST_READONLY_PASSWORD_VM_PARAM_NAME, "pass"); - System.setProperty( - SolrZkClient.ZK_ACL_PROVIDER_CLASS_NAME_VM_PARAM_NAME, - VMParamsAllAndReadonlyDigestZkACLProvider.class.getName()); - System.setProperty( - VMParamsAllAndReadonlyDigestZkACLProvider.DEFAULT_DIGEST_READONLY_USERNAME_VM_PARAM_NAME, - "user"); - System.setProperty( - VMParamsAllAndReadonlyDigestZkACLProvider.DEFAULT_DIGEST_READONLY_PASSWORD_VM_PARAM_NAME, - "pass"); String[] args = new String[] {"updateacls", "/", "-z", zkServer.getZkAddress()}; assertEquals(0, CLITestHelper.runTool(args, UpdateACLTool.class)); @@ -593,7 +587,7 @@ public void tearDown() throws Exception { if (zkServer != null) { zkServer.shutdown(); } - System.setOut(originalSystemOut); + // System.setOut(originalSystemOut); super.tearDown(); } }