diff --git a/app/src/main/java/org/astraea/argument/Argument.java b/app/src/main/java/org/astraea/argument/Argument.java new file mode 100644 index 0000000000..b8ea149a6f --- /dev/null +++ b/app/src/main/java/org/astraea/argument/Argument.java @@ -0,0 +1,74 @@ +package org.astraea.argument; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.UnixStyleUsageFormatter; +import java.io.*; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; +import org.apache.kafka.clients.CommonClientConfigs; + +/** This basic argument defines the common property used by all kafka clients. */ +public abstract class Argument { + + /** + * Side effect: parse args into toolArgument + * + * @param toolArgument An argument object that the user want. + * @param args Command line arguments that are put into main function. + */ + public static T parse(T toolArgument, String[] args) { + JCommander jc = JCommander.newBuilder().addObject(toolArgument).build(); + jc.setUsageFormatter(new UnixStyleUsageFormatter(jc)); + try { + jc.parse(args); + } catch (ParameterException pe) { + var sb = new StringBuilder(); + jc.getUsageFormatter().usage(sb); + throw new ParameterException(pe.getMessage() + "\n" + sb); + } + return toolArgument; + } + + @Parameter( + names = {"--bootstrap.servers"}, + description = "String: server to connect to", + validateWith = NonEmptyStringField.class, + required = true) + public String brokers; + + /** + * @param propsFile file path containing the properties to be passed to kafka + * @return the kafka props consists of bootstrap servers and all props from file (if it is + * existent) + */ + Map properties(String propsFile) { + var props = new Properties(); + if (propsFile != null) { + try (var input = new FileInputStream(propsFile)) { + props.load(input); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + props.setProperty(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, brokers); + return props.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey().toString(), Map.Entry::getValue)); + } + + @Parameter( + names = {"--prop.file"}, + description = "the file path containing the properties to be passed to kafka admin", + validateWith = NonEmptyStringField.class) + public String propFile; + + /** + * @return the kafka props consists of bootstrap servers and all props from file (if it is + * existent) + */ + public Map props() { + return properties(propFile); + } +} diff --git a/app/src/main/java/org/astraea/argument/ArgumentUtil.java b/app/src/main/java/org/astraea/argument/ArgumentUtil.java deleted file mode 100644 index 70a0a30085..0000000000 --- a/app/src/main/java/org/astraea/argument/ArgumentUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.astraea.argument; - -import com.beust.jcommander.JCommander; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.UnixStyleUsageFormatter; - -/* - * A tool used to parse command line arguments. - * - * To add new option, add new option in the corresponding file. - * @Parameter(names={"--option"}, description="") - * public ; - * */ - -public class ArgumentUtil { - // Do not instantiate. - private ArgumentUtil() {} - - /** - * Side effect: parse args into toolArgument - * - * @param toolArgument An argument object that the user want. - * @param args Command line arguments that are put into main function. - */ - public static T parseArgument(T toolArgument, String[] args) { - JCommander jc = JCommander.newBuilder().addObject(toolArgument).build(); - jc.setUsageFormatter(new UnixStyleUsageFormatter(jc)); - try { - jc.parse(args); - } catch (ParameterException pe) { - var sb = new StringBuilder(); - jc.getUsageFormatter().usage(sb); - throw new ParameterException(pe.getMessage() + "\n" + sb); - } - return toolArgument; - } -} diff --git a/app/src/main/java/org/astraea/argument/BasicArgument.java b/app/src/main/java/org/astraea/argument/BasicArgument.java deleted file mode 100644 index 5d1c9c8dfc..0000000000 --- a/app/src/main/java/org/astraea/argument/BasicArgument.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.astraea.argument; - -import com.beust.jcommander.Parameter; -import java.io.*; -import java.util.Map; -import java.util.Properties; -import java.util.stream.Collectors; -import org.apache.kafka.clients.CommonClientConfigs; -import org.astraea.argument.validator.NotEmptyString; - -/** This basic argument defines the common property used by all kafka clients. */ -public abstract class BasicArgument { - @Parameter( - names = {"--bootstrap.servers"}, - description = "String: server to connect to", - validateWith = NotEmptyString.class, - required = true) - public String brokers; - - /** - * @param propsFile file path containing the properties to be passed to kafka - * @return the kafka props consists of bootstrap servers and all props from file (if it is - * existent) - */ - protected Map properties(String propsFile) { - var props = new Properties(); - if (propsFile != null) { - try (var input = new FileInputStream(propsFile)) { - props.load(input); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - props.setProperty(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, brokers); - return props.entrySet().stream() - .collect(Collectors.toMap(e -> e.getKey().toString(), Map.Entry::getValue)); - } -} diff --git a/app/src/main/java/org/astraea/argument/BasicArgumentWithPropFile.java b/app/src/main/java/org/astraea/argument/BasicArgumentWithPropFile.java deleted file mode 100644 index 4414fb4887..0000000000 --- a/app/src/main/java/org/astraea/argument/BasicArgumentWithPropFile.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.astraea.argument; - -import com.beust.jcommander.Parameter; -import java.util.Map; -import org.astraea.argument.validator.NotEmptyString; - -public abstract class BasicArgumentWithPropFile extends BasicArgument { - @Parameter( - names = {"--prop.file"}, - description = "the file path containing the properties to be passed to kafka admin", - validateWith = NotEmptyString.class) - public String propFile; - - /** - * @return the kafka props consists of bootstrap servers and all props from file (if it is - * existent) - */ - public Map props() { - return properties(propFile); - } -} diff --git a/app/src/main/java/org/astraea/argument/BooleanField.java b/app/src/main/java/org/astraea/argument/BooleanField.java new file mode 100644 index 0000000000..86165c3565 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/BooleanField.java @@ -0,0 +1,17 @@ +package org.astraea.argument; + +import com.beust.jcommander.ParameterException; + +public class BooleanField implements NonEmptyField { + @Override + public Boolean convert(String value) { + return Boolean.parseBoolean(value); + } + + @Override + public void validate(String name, String value) throws ParameterException { + NonEmptyField.super.validate(name, value); + if (!("false".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value))) + throw new ParameterException(value + " is not boolean type"); + } +} diff --git a/app/src/main/java/org/astraea/argument/CompressionField.java b/app/src/main/java/org/astraea/argument/CompressionField.java new file mode 100644 index 0000000000..f5df778eb8 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/CompressionField.java @@ -0,0 +1,34 @@ +package org.astraea.argument; + +import com.beust.jcommander.ParameterException; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.kafka.common.record.CompressionType; + +public class CompressionField implements NonEmptyField { + /** + * @param value Name of compression type. Accept lower-case name only ("none", "gzip", "snappy", + * "lz4", "zstd"). + */ + @Override + public CompressionType convert(String value) { + try { + // `CompressionType#forName` accept lower-case name only. + return CompressionType.forName(value); + } catch (IllegalArgumentException e) { + throw new ParameterException( + "the " + + value + + " is unsupported. The supported algorithms are " + + Stream.of(CompressionType.values()) + .map(CompressionType::name) + .collect(Collectors.joining(","))); + } + } + + @Override + public void validate(String name, String value) throws ParameterException { + NonEmptyField.super.validate(name, value); + convert(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/converter/DurationConverter.java b/app/src/main/java/org/astraea/argument/DurationField.java similarity index 91% rename from app/src/main/java/org/astraea/argument/converter/DurationConverter.java rename to app/src/main/java/org/astraea/argument/DurationField.java index 3afdbd406c..6049a47847 100644 --- a/app/src/main/java/org/astraea/argument/converter/DurationConverter.java +++ b/app/src/main/java/org/astraea/argument/DurationField.java @@ -1,7 +1,5 @@ -package org.astraea.argument.converter; +package org.astraea.argument; -import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.IStringConverter; import com.beust.jcommander.ParameterException; import java.time.Duration; import java.util.regex.Matcher; @@ -42,7 +40,7 @@ *
  • (doesn't work) {@code "0.5" to {@code Duration.ofMillis(500)}} * */ -public class DurationConverter implements IStringConverter, IParameterValidator { +public class DurationField implements Field { static final Pattern TIME_PATTERN = Pattern.compile("^(?[0-9]+)(?days|day|h|m|s|ms|us|ns|)$"); diff --git a/app/src/main/java/org/astraea/argument/Field.java b/app/src/main/java/org/astraea/argument/Field.java new file mode 100644 index 0000000000..1b28159464 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/Field.java @@ -0,0 +1,21 @@ +package org.astraea.argument; + +import com.beust.jcommander.IParameterValidator; +import com.beust.jcommander.IStringConverter; +import com.beust.jcommander.ParameterException; + +public interface Field extends IStringConverter, IParameterValidator { + + /** @return an object of type T created from the parameter value. */ + @Override + T convert(String value); + + /** + * Validate the parameter. + * + * @param name The name of the parameter (e.g. "-host"). + * @param value The value of the parameter that we need to validate + * @throws ParameterException Thrown if the value of the parameter is invalid. + */ + void validate(String name, String value) throws ParameterException; +} diff --git a/app/src/main/java/org/astraea/argument/IntegerSetField.java b/app/src/main/java/org/astraea/argument/IntegerSetField.java new file mode 100644 index 0000000000..8344d81c4f --- /dev/null +++ b/app/src/main/java/org/astraea/argument/IntegerSetField.java @@ -0,0 +1,12 @@ +package org.astraea.argument; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class IntegerSetField implements SetField { + @Override + public Set convert(String value) { + return Stream.of(value.split(SEPARATOR)).map(Integer::valueOf).collect(Collectors.toSet()); + } +} diff --git a/app/src/main/java/org/astraea/argument/NonEmptyField.java b/app/src/main/java/org/astraea/argument/NonEmptyField.java new file mode 100644 index 0000000000..4c89054d91 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/NonEmptyField.java @@ -0,0 +1,12 @@ +package org.astraea.argument; + +import com.beust.jcommander.ParameterException; + +public interface NonEmptyField extends Field { + + @Override + default void validate(String name, String value) throws ParameterException { + if (value == null || value.isBlank()) + throw new ParameterException(name + " should not be empty."); + } +} diff --git a/app/src/main/java/org/astraea/argument/NonEmptyStringField.java b/app/src/main/java/org/astraea/argument/NonEmptyStringField.java new file mode 100644 index 0000000000..0837b85d01 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/NonEmptyStringField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class NonEmptyStringField implements NonEmptyField { + @Override + public String convert(String value) { + return value; + } +} diff --git a/app/src/main/java/org/astraea/argument/NonNegativeDoubleField.java b/app/src/main/java/org/astraea/argument/NonNegativeDoubleField.java new file mode 100644 index 0000000000..f063a4f95a --- /dev/null +++ b/app/src/main/java/org/astraea/argument/NonNegativeDoubleField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class NonNegativeDoubleField implements PositiveNumberField { + @Override + public Double convert(String value) { + return Double.parseDouble(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/NonNegativeIntegerField.java b/app/src/main/java/org/astraea/argument/NonNegativeIntegerField.java new file mode 100644 index 0000000000..2b6b87acfa --- /dev/null +++ b/app/src/main/java/org/astraea/argument/NonNegativeIntegerField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class NonNegativeIntegerField implements PositiveNumberField { + @Override + public Integer convert(String value) { + return Integer.parseInt(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/NonNegativeLongField.java b/app/src/main/java/org/astraea/argument/NonNegativeLongField.java new file mode 100644 index 0000000000..ff4f7566ff --- /dev/null +++ b/app/src/main/java/org/astraea/argument/NonNegativeLongField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class NonNegativeLongField implements PositiveNumberField { + @Override + public Long convert(String value) { + return Long.parseLong(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/NonNegativeNumberField.java b/app/src/main/java/org/astraea/argument/NonNegativeNumberField.java new file mode 100644 index 0000000000..84da918824 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/NonNegativeNumberField.java @@ -0,0 +1,12 @@ +package org.astraea.argument; + +import com.beust.jcommander.ParameterException; + +public interface NonNegativeNumberField extends NonEmptyField { + + @Override + default void validate(String name, String value) throws ParameterException { + NonEmptyField.super.validate(name, value); + if (Long.parseLong(value) < 0) throw new ParameterException(name + " should not be negative."); + } +} diff --git a/app/src/main/java/org/astraea/argument/NonNegativeShortField.java b/app/src/main/java/org/astraea/argument/NonNegativeShortField.java new file mode 100644 index 0000000000..57ea327337 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/NonNegativeShortField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class NonNegativeShortField implements PositiveNumberField { + @Override + public Short convert(String value) { + return Short.parseShort(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/PositiveDoubleField.java b/app/src/main/java/org/astraea/argument/PositiveDoubleField.java new file mode 100644 index 0000000000..0a97144d4c --- /dev/null +++ b/app/src/main/java/org/astraea/argument/PositiveDoubleField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class PositiveDoubleField implements PositiveNumberField { + @Override + public Double convert(String value) { + return Double.parseDouble(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/PositiveIntegerField.java b/app/src/main/java/org/astraea/argument/PositiveIntegerField.java new file mode 100644 index 0000000000..3866974378 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/PositiveIntegerField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class PositiveIntegerField implements PositiveNumberField { + @Override + public Integer convert(String value) { + return Integer.parseInt(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/PositiveLongField.java b/app/src/main/java/org/astraea/argument/PositiveLongField.java new file mode 100644 index 0000000000..79d5cdcd4c --- /dev/null +++ b/app/src/main/java/org/astraea/argument/PositiveLongField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class PositiveLongField implements PositiveNumberField { + @Override + public Long convert(String value) { + return Long.parseLong(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/PositiveNumberField.java b/app/src/main/java/org/astraea/argument/PositiveNumberField.java new file mode 100644 index 0000000000..d6ac1c1c6e --- /dev/null +++ b/app/src/main/java/org/astraea/argument/PositiveNumberField.java @@ -0,0 +1,11 @@ +package org.astraea.argument; + +import com.beust.jcommander.ParameterException; + +public interface PositiveNumberField extends Field { + + @Override + default void validate(String name, String value) throws ParameterException { + if (Long.parseLong(value) <= 0) throw new ParameterException(name + " should be positive."); + } +} diff --git a/app/src/main/java/org/astraea/argument/PositiveShortField.java b/app/src/main/java/org/astraea/argument/PositiveShortField.java new file mode 100644 index 0000000000..e9f04ba807 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/PositiveShortField.java @@ -0,0 +1,8 @@ +package org.astraea.argument; + +public class PositiveShortField implements PositiveNumberField { + @Override + public Short convert(String value) { + return Short.parseShort(value); + } +} diff --git a/app/src/main/java/org/astraea/argument/SetField.java b/app/src/main/java/org/astraea/argument/SetField.java new file mode 100644 index 0000000000..e543ff7579 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/SetField.java @@ -0,0 +1,14 @@ +package org.astraea.argument; + +import com.beust.jcommander.ParameterException; +import java.util.Set; + +public interface SetField extends Field> { + String SEPARATOR = ","; + + @Override + default void validate(String name, String value) throws ParameterException { + if (value == null || value.isBlank() || Set.of(value.split(SEPARATOR)).isEmpty()) + throw new ParameterException("set type can't be empty"); + } +} diff --git a/app/src/main/java/org/astraea/argument/StringSetField.java b/app/src/main/java/org/astraea/argument/StringSetField.java new file mode 100644 index 0000000000..5f74f69478 --- /dev/null +++ b/app/src/main/java/org/astraea/argument/StringSetField.java @@ -0,0 +1,11 @@ +package org.astraea.argument; + +import java.util.Set; + +public class StringSetField implements SetField { + + @Override + public Set convert(String value) { + return Set.of(value.split(SEPARATOR)); + } +} diff --git a/app/src/main/java/org/astraea/argument/converter/IntegerSetConverter.java b/app/src/main/java/org/astraea/argument/converter/IntegerSetConverter.java deleted file mode 100644 index e87c21b28e..0000000000 --- a/app/src/main/java/org/astraea/argument/converter/IntegerSetConverter.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.astraea.argument.converter; - -import com.beust.jcommander.IStringConverter; -import com.beust.jcommander.ParameterException; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class IntegerSetConverter implements IStringConverter> { - @Override - public Set convert(String value) { - if (value.isEmpty()) throw new ParameterException("array type can't be empty"); - return Stream.of(value.split(",")).map(Integer::valueOf).collect(Collectors.toSet()); - } -} diff --git a/app/src/main/java/org/astraea/argument/converter/ShortConverter.java b/app/src/main/java/org/astraea/argument/converter/ShortConverter.java deleted file mode 100644 index b8390649bd..0000000000 --- a/app/src/main/java/org/astraea/argument/converter/ShortConverter.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.astraea.argument.converter; - -import com.beust.jcommander.IStringConverter; - -public class ShortConverter implements IStringConverter { - @Override - public Short convert(String value) { - return Short.valueOf(value); - } -} diff --git a/app/src/main/java/org/astraea/argument/converter/StringSetConverter.java b/app/src/main/java/org/astraea/argument/converter/StringSetConverter.java deleted file mode 100644 index 8acf49793d..0000000000 --- a/app/src/main/java/org/astraea/argument/converter/StringSetConverter.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.astraea.argument.converter; - -import com.beust.jcommander.IStringConverter; -import com.beust.jcommander.ParameterException; -import java.util.Set; - -public class StringSetConverter implements IStringConverter> { - @Override - public Set convert(String value) { - if (value.isEmpty()) throw new ParameterException("array type can't be empty"); - return Set.of(value.split(",")); - } -} diff --git a/app/src/main/java/org/astraea/argument/validator/NonNegativeLong.java b/app/src/main/java/org/astraea/argument/validator/NonNegativeLong.java deleted file mode 100644 index 5a01a4321e..0000000000 --- a/app/src/main/java/org/astraea/argument/validator/NonNegativeLong.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.astraea.argument.validator; - -import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.ParameterException; - -public class NonNegativeLong implements IParameterValidator { - @Override - public void validate(String name, String value) throws ParameterException { - if (Long.parseLong(value) < 0) throw new ParameterException(name + " should not be negative."); - } -} diff --git a/app/src/main/java/org/astraea/argument/validator/NotEmptyString.java b/app/src/main/java/org/astraea/argument/validator/NotEmptyString.java deleted file mode 100644 index 078169a313..0000000000 --- a/app/src/main/java/org/astraea/argument/validator/NotEmptyString.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.astraea.argument.validator; - -import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.ParameterException; - -public class NotEmptyString implements IParameterValidator { - @Override - public void validate(String name, String value) throws ParameterException { - if (value == null || value.equals("")) - throw new ParameterException(name + " should not be empty."); - } -} diff --git a/app/src/main/java/org/astraea/argument/validator/PositiveLong.java b/app/src/main/java/org/astraea/argument/validator/PositiveLong.java deleted file mode 100644 index c41fc60ddc..0000000000 --- a/app/src/main/java/org/astraea/argument/validator/PositiveLong.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.astraea.argument.validator; - -import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.ParameterException; - -public class PositiveLong implements IParameterValidator { - @Override - public void validate(String name, String value) throws ParameterException { - if (Long.parseLong(value) <= 0) throw new ParameterException(name + " should be positive."); - } -} diff --git a/app/src/main/java/org/astraea/automation/Automation.java b/app/src/main/java/org/astraea/automation/Automation.java index d89ee564f5..8a0679d3ba 100644 --- a/app/src/main/java/org/astraea/automation/Automation.java +++ b/app/src/main/java/org/astraea/automation/Automation.java @@ -10,8 +10,7 @@ import org.apache.kafka.clients.admin.AdminClient; import org.apache.kafka.clients.admin.AdminClientConfig; import org.apache.kafka.clients.admin.KafkaAdminClient; -import org.astraea.argument.ArgumentUtil; -import org.astraea.argument.validator.NotEmptyString; +import org.astraea.argument.NonEmptyStringField; import org.astraea.performance.Performance; /** @@ -42,7 +41,7 @@ public class Automation { public static void main(String[] args) { try { var properties = new Properties(); - var arg = ArgumentUtil.parseArgument(new automationArgument(), args); + var arg = org.astraea.argument.Argument.parse(new Argument(), args); properties.load(new FileInputStream(arg.address)); var whetherDeleteTopic = properties.getProperty("--whetherDeleteTopic").equals("true"); var bootstrap = properties.getProperty("--bootstrap.servers"); @@ -57,7 +56,7 @@ public static void main(String[] args) { while (i < times) { var str = Performance.execute( - ArgumentUtil.parseArgument( + org.astraea.argument.Argument.parse( new Performance.Argument(), performanceArgs(properties))); i++; if (whetherDeleteTopic) { @@ -88,11 +87,11 @@ private static String[] performanceArgs(Properties properties) { return args.toArray(strings); } - private static class automationArgument { + private static class Argument { @Parameter( names = {"--file"}, description = "String: automation.properties address", - validateWith = NotEmptyString.class) + validateWith = NonEmptyStringField.class) String address = ""; } } diff --git a/app/src/main/java/org/astraea/metrics/MetricExplorer.java b/app/src/main/java/org/astraea/metrics/MetricExplorer.java index 0459e30613..1ba15b0e74 100644 --- a/app/src/main/java/org/astraea/metrics/MetricExplorer.java +++ b/app/src/main/java/org/astraea/metrics/MetricExplorer.java @@ -1,11 +1,14 @@ package org.astraea.metrics; import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.IStringConverter; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import java.net.MalformedURLException; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -13,8 +16,7 @@ import java.util.stream.Stream; import javax.management.openmbean.CompositeDataSupport; import javax.management.remote.JMXServiceURL; -import org.astraea.argument.ArgumentUtil; -import org.astraea.argument.validator.NotEmptyString; +import org.astraea.argument.NonEmptyField; import org.astraea.metrics.jmx.BeanObject; import org.astraea.metrics.jmx.BeanQuery; import org.astraea.metrics.jmx.MBeanClient; @@ -164,7 +166,7 @@ static List sortBeanObjects( } public static void main(String[] args) { - var arguments = ArgumentUtil.parseArgument(new Argument(), args); + var arguments = org.astraea.argument.Argument.parse(new Argument(), args); try (var mBeanClient = MBeanClient.of(arguments.jmxServer)) { execute(mBeanClient, arguments); @@ -176,8 +178,8 @@ static class Argument { names = {"--jmx.server"}, description = "The JMX server address to connect to, support [hostname:port] style or JMX URI format", - validateWith = NotEmptyString.class, - converter = Argument.JmxServerUrlConverter.class, + validateWith = JmxServerUrlField.class, + converter = JmxServerUrlField.class, required = true) JMXServiceURL jmxServer; @@ -204,7 +206,7 @@ static class Argument { description = "Show the list view of MBeans' domain name & properties") boolean viewObjectNameList = false; - public static class JmxServerUrlConverter implements IStringConverter { + public static class JmxServerUrlField implements NonEmptyField { /** This regex used to test if a string look like a JMX URL */ static Pattern patternOfJmxUrlStart = Pattern.compile("^service:jmx:rmi://"); diff --git a/app/src/main/java/org/astraea/performance/Distribution.java b/app/src/main/java/org/astraea/performance/Distribution.java index bbfc59556a..0942b253bf 100644 --- a/app/src/main/java/org/astraea/performance/Distribution.java +++ b/app/src/main/java/org/astraea/performance/Distribution.java @@ -1,18 +1,18 @@ package org.astraea.performance; -import com.beust.jcommander.IStringConverter; import com.beust.jcommander.ParameterException; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.stream.IntStream; +import org.astraea.argument.NonEmptyField; /** Randomly generate a long number with respect to some distribution */ public interface Distribution { long get(); - class DistributionConverter implements IStringConverter { + class DistributionField implements NonEmptyField { @Override public Distribution convert(String rawArgument) { var args = rawArgument.split(":"); diff --git a/app/src/main/java/org/astraea/performance/ExeTime.java b/app/src/main/java/org/astraea/performance/ExeTime.java index 4506243671..07bc7f6e3b 100644 --- a/app/src/main/java/org/astraea/performance/ExeTime.java +++ b/app/src/main/java/org/astraea/performance/ExeTime.java @@ -1,12 +1,11 @@ package org.astraea.performance; -import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.IStringConverter; import com.beust.jcommander.ParameterException; import java.time.Duration; import java.util.function.BiFunction; import java.util.regex.Pattern; -import org.astraea.argument.converter.DurationConverter; +import org.astraea.argument.DurationField; +import org.astraea.argument.NonEmptyField; /** * Two kind of running modes. One runs for a duration of time. The other runs for a number of @@ -21,7 +20,7 @@ static ExeTime of(String exeTime) { final long records = Long.parseLong(exeTime.replace("records", "")); return ExeTime.of((completeRecords, ignore) -> 100D * completeRecords / records, exeTime); } - final Duration duration = new DurationConverter().convert(exeTime); + final Duration duration = new DurationField().convert(exeTime); return ExeTime.of((ignore, elapsedTime) -> 100D * elapsedTime / duration.toMillis(), exeTime); } @@ -39,14 +38,7 @@ public String toString() { }; } - class Converter implements IStringConverter { - @Override - public ExeTime convert(String value) { - return ExeTime.of(value); - } - } - - class Validator implements IParameterValidator { + class Field implements NonEmptyField { static final Pattern PATTERN = Pattern.compile("^([0-9]+)(days|day|h|m|s|ms|us|ns|records)$"); @Override @@ -56,5 +48,10 @@ public void validate(String name, String value) throws ParameterException { "Invalid ExeTime format. valid format example: \"1m\" or \"89242records\""); } } + + @Override + public ExeTime convert(String value) { + return ExeTime.of(value); + } } } diff --git a/app/src/main/java/org/astraea/performance/Performance.java b/app/src/main/java/org/astraea/performance/Performance.java index 96a5978c1e..4ada2f0083 100644 --- a/app/src/main/java/org/astraea/performance/Performance.java +++ b/app/src/main/java/org/astraea/performance/Performance.java @@ -1,8 +1,6 @@ package org.astraea.performance; -import com.beust.jcommander.IStringConverter; import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; import java.io.IOException; import java.time.Duration; import java.util.Collection; @@ -17,19 +15,17 @@ import java.util.function.BiConsumer; import java.util.stream.Collectors; import java.util.stream.IntStream; -import java.util.stream.Stream; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.internals.DefaultPartitioner; import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.errors.WakeupException; import org.apache.kafka.common.record.CompressionType; import org.astraea.Utils; -import org.astraea.argument.ArgumentUtil; -import org.astraea.argument.BasicArgumentWithPropFile; -import org.astraea.argument.converter.ShortConverter; -import org.astraea.argument.validator.NonNegativeLong; -import org.astraea.argument.validator.NotEmptyString; -import org.astraea.argument.validator.PositiveLong; +import org.astraea.argument.CompressionField; +import org.astraea.argument.NonEmptyStringField; +import org.astraea.argument.NonNegativeLongField; +import org.astraea.argument.PositiveLongField; +import org.astraea.argument.PositiveShortField; import org.astraea.concurrent.ThreadPool; import org.astraea.consumer.Consumer; import org.astraea.producer.Producer; @@ -67,7 +63,7 @@ public class Performance { /** Used in Automation, to achieve the end of one Performance and then start another. */ public static void main(String[] args) throws InterruptedException, IOException, ExecutionException { - execute(ArgumentUtil.parseArgument(new Argument(), args)); + execute(org.astraea.argument.Argument.parse(new Argument(), args)); } public static Future execute(final Argument param) @@ -233,37 +229,39 @@ private static boolean positiveSpecifyBroker(Argument param) { return param.specifyBroker.stream().allMatch(broker -> broker >= 0); } - public static class Argument extends BasicArgumentWithPropFile { + public static class Argument extends org.astraea.argument.Argument { @Parameter( names = {"--topic"}, description = "String: topic name", - validateWith = NotEmptyString.class) + validateWith = NonEmptyStringField.class) String topic = "testPerformance-" + System.currentTimeMillis(); @Parameter( names = {"--partitions"}, description = "Integer: number of partitions to create the topic", - validateWith = PositiveLong.class) + validateWith = PositiveLongField.class) int partitions = 1; @Parameter( names = {"--replicas"}, description = "Integer: number of replica to create the topic", - validateWith = PositiveLong.class, - converter = ShortConverter.class) + validateWith = PositiveShortField.class, + converter = PositiveShortField.class) short replicas = 1; @Parameter( names = {"--producers"}, description = "Integer: number of producers to produce records", - validateWith = PositiveLong.class) + validateWith = PositiveShortField.class, + converter = PositiveShortField.class) int producers = 1; @Parameter( names = {"--consumers"}, description = "Integer: number of consumers to consume records", - validateWith = NonNegativeLong.class) + validateWith = NonNegativeLongField.class, + converter = NonNegativeLongField.class) int consumers = 1; @Parameter( @@ -273,8 +271,8 @@ public static class Argument extends BasicArgumentWithPropFile { + " The duration formats accepted are (a number) + (a time unit)." + " The time units can be \"days\", \"day\", \"h\", \"m\", \"s\", \"ms\"," + " \"us\", \"ns\"", - validateWith = ExeTime.Validator.class, - converter = ExeTime.Converter.class) + validateWith = ExeTime.Field.class, + converter = ExeTime.Field.class) ExeTime exeTime = ExeTime.of("1000records"); @Parameter( @@ -285,20 +283,20 @@ public static class Argument extends BasicArgumentWithPropFile { @Parameter( names = {"--record.size"}, description = "DataSize: size of each record. e.g. \"500KiB\"", - converter = DataSize.Converter.class) + converter = DataSize.Field.class) DataSize recordSize = DataUnit.KiB.of(1); @Parameter( names = {"--jmx.servers"}, description = "String: server to get jmx metrics @[,@]*", - validateWith = NotEmptyString.class) + validateWith = NonEmptyStringField.class) String jmxServers = ""; @Parameter( names = {"--partitioner"}, description = "String: the full class name of the desired partitioner", - validateWith = NotEmptyString.class) + validateWith = NonEmptyStringField.class) String partitioner = DefaultPartitioner.class.getName(); public Map producerProps() { @@ -317,44 +315,21 @@ public Map producerProps() { names = {"--compression"}, description = "String: the compression algorithm used by producer. Available algorithm are none, gzip, snappy, lz4, and zstd", - converter = CompressionArgument.class) + converter = CompressionField.class) CompressionType compression = CompressionType.NONE; @Parameter( names = {"--key.distribution"}, description = "String: Distribution name. Available distribution names: \"fixed\" \"uniform\", \"zipfian\", \"latest\". Default: uniform", - converter = Distribution.DistributionConverter.class) + converter = Distribution.DistributionField.class) Distribution distribution = Distribution.uniform(); @Parameter( names = {"--specify.broker"}, description = "String: Used with SpecifyBrokerPartitioner to specify the brokers that partitioner can send.", - validateWith = NotEmptyString.class) + validateWith = NonEmptyStringField.class) List specifyBroker = List.of(-1); } - - static class CompressionArgument implements IStringConverter { - - /** - * @param value Name of compression type. Accept lower-case name only ("none", "gzip", "snappy", - * "lz4", "zstd"). - */ - @Override - public CompressionType convert(String value) { - try { - // `CompressionType#forName` accept lower-case name only. - return CompressionType.forName(value); - } catch (IllegalArgumentException e) { - throw new ParameterException( - "the " - + value - + " is unsupported. The supported algorithms are " - + Stream.of(CompressionType.values()) - .map(CompressionType::name) - .collect(Collectors.joining(","))); - } - } - } } diff --git a/app/src/main/java/org/astraea/topic/ReplicaCollie.java b/app/src/main/java/org/astraea/topic/ReplicaCollie.java index 6ebb658e5d..8f7029733b 100644 --- a/app/src/main/java/org/astraea/topic/ReplicaCollie.java +++ b/app/src/main/java/org/astraea/topic/ReplicaCollie.java @@ -1,16 +1,18 @@ package org.astraea.topic; import com.beust.jcommander.Parameter; -import com.beust.jcommander.converters.BooleanConverter; import java.io.IOException; -import java.util.*; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; import java.util.stream.Collectors; import org.apache.kafka.common.TopicPartition; -import org.astraea.argument.ArgumentUtil; -import org.astraea.argument.BasicArgumentWithPropFile; -import org.astraea.argument.converter.IntegerSetConverter; -import org.astraea.argument.converter.StringSetConverter; -import org.astraea.argument.validator.NotEmptyString; +import org.astraea.argument.BooleanField; +import org.astraea.argument.IntegerSetField; +import org.astraea.argument.StringSetField; public class ReplicaCollie { static final String UNKNOWN = "unknown"; @@ -276,7 +278,7 @@ static Map execute(TopicAdmin admin, Argument args } public static void main(String[] args) throws IOException { - var argument = ArgumentUtil.parseArgument(new Argument(), args); + var argument = org.astraea.argument.Argument.parse(new Argument(), args); try (var admin = TopicAdmin.of(argument.props())) { execute(admin, argument) .forEach( @@ -297,49 +299,49 @@ public static void main(String[] args) throws IOException { } } - static class Argument extends BasicArgumentWithPropFile { + static class Argument extends org.astraea.argument.Argument { @Parameter( names = {"--topics"}, description = "Those topics' partitions will get reassigned. Empty means all topics", - validateWith = NotEmptyString.class, - converter = StringSetConverter.class) + validateWith = StringSetField.class, + converter = StringSetField.class) public Set topics = Collections.emptySet(); @Parameter( names = {"--from"}, description = "Those brokers won't hold any replicas of topics (defined by --topics)", - validateWith = NotEmptyString.class, - converter = IntegerSetConverter.class, + validateWith = IntegerSetField.class, + converter = IntegerSetField.class, required = true) Set fromBrokers; @Parameter( names = {"--to"}, description = "The replicas of topics (defined by --topic) will be moved to those brokers", - validateWith = NotEmptyString.class, - converter = IntegerSetConverter.class) + validateWith = IntegerSetField.class, + converter = IntegerSetField.class) Set toBrokers = Collections.emptySet(); @Parameter( names = {"--partitions"}, description = "all partitions that will be moved", - validateWith = NotEmptyString.class, - converter = IntegerSetConverter.class) + validateWith = IntegerSetField.class, + converter = IntegerSetField.class) Set partitions = Collections.emptySet(); @Parameter( names = {"--path"}, description = "The partition that will be moved to", - validateWith = NotEmptyString.class, - converter = StringSetConverter.class) + validateWith = StringSetField.class, + converter = StringSetField.class) Set path = Collections.emptySet(); @Parameter( names = {"--verify"}, description = "True if you just want to see the new assignment instead of executing the plan", - validateWith = NotEmptyString.class, - converter = BooleanConverter.class) + validateWith = BooleanField.class, + converter = BooleanField.class) boolean verify = false; } } diff --git a/app/src/main/java/org/astraea/topic/ReplicaSyncingMonitor.java b/app/src/main/java/org/astraea/topic/ReplicaSyncingMonitor.java index de62b15469..d77b10bd7e 100644 --- a/app/src/main/java/org/astraea/topic/ReplicaSyncingMonitor.java +++ b/app/src/main/java/org/astraea/topic/ReplicaSyncingMonitor.java @@ -13,10 +13,8 @@ import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.TopicPartitionReplica; import org.astraea.Utils; -import org.astraea.argument.ArgumentUtil; -import org.astraea.argument.BasicArgumentWithPropFile; -import org.astraea.argument.converter.DurationConverter; -import org.astraea.argument.validator.NotEmptyString; +import org.astraea.argument.DurationField; +import org.astraea.argument.NonEmptyStringField; import org.astraea.utils.DataRate; import org.astraea.utils.DataSize; import org.astraea.utils.DataUnit; @@ -24,7 +22,7 @@ public class ReplicaSyncingMonitor { public static void main(String[] args) { - Argument argument = ArgumentUtil.parseArgument(new Argument(), args); + Argument argument = org.astraea.argument.Argument.parse(new Argument(), args); try (TopicAdmin topicAdmin = TopicAdmin.of(argument.props())) { execute(topicAdmin, argument); } @@ -295,12 +293,12 @@ static String replicaDescriptor(Replica replica) { .collect(Collectors.joining(", ", "[", "]")); } - static class Argument extends BasicArgumentWithPropFile { + static class Argument extends org.astraea.argument.Argument { @Parameter( names = {"--topics"}, description = "String: topics to track, use all non-synced topics by default", - validateWith = NotEmptyString.class) + validateWith = NonEmptyStringField.class) public Set topics = Set.of(); @Parameter( @@ -314,8 +312,8 @@ static class Argument extends BasicArgumentWithPropFile { description = "Time: the time interval between replica state check, support multiple time unit like 10s, 500ms and 100us. " + "If no time unit specified, second unit will be used.", - validateWith = DurationConverter.class, - converter = DurationConverter.class) + validateWith = DurationField.class, + converter = DurationField.class) public Duration interval = Duration.ofSeconds(1); } } diff --git a/app/src/main/java/org/astraea/topic/TopicExplorer.java b/app/src/main/java/org/astraea/topic/TopicExplorer.java index acb801cb79..e0b9d1c8fa 100644 --- a/app/src/main/java/org/astraea/topic/TopicExplorer.java +++ b/app/src/main/java/org/astraea/topic/TopicExplorer.java @@ -11,10 +11,8 @@ import java.util.stream.IntStream; import java.util.stream.Stream; import org.apache.kafka.common.TopicPartition; -import org.astraea.argument.ArgumentUtil; -import org.astraea.argument.BasicArgumentWithPropFile; -import org.astraea.argument.converter.StringSetConverter; -import org.astraea.argument.validator.NotEmptyString; +import org.astraea.argument.NonEmptyStringField; +import org.astraea.argument.StringSetField; import org.astraea.utils.DataSize; import org.astraea.utils.DataUnit; @@ -92,7 +90,7 @@ static Result execute(TopicAdmin admin, Set topics) { } public static void main(String[] args) throws IOException { - var argument = ArgumentUtil.parseArgument(new Argument(), args); + var argument = org.astraea.argument.Argument.parse(new Argument(), args); try (var admin = TopicAdmin.of(argument.props())) { var result = execute(admin, argument.topics.isEmpty() ? admin.topicNames() : argument.topics); TreeOutput.print(result, System.out); @@ -422,12 +420,12 @@ static String descriptor(Replica replica) { } } - static class Argument extends BasicArgumentWithPropFile { + static class Argument extends org.astraea.argument.Argument { @Parameter( names = {"--topics"}, description = "the topics to show all offset-related information. Empty means all topics", - validateWith = NotEmptyString.class, - converter = StringSetConverter.class) + validateWith = NonEmptyStringField.class, + converter = StringSetField.class) public Set topics = Collections.emptySet(); } } diff --git a/app/src/main/java/org/astraea/topic/cost/PartitionScore.java b/app/src/main/java/org/astraea/topic/cost/PartitionScore.java index 40def4f638..9cae91b4b6 100644 --- a/app/src/main/java/org/astraea/topic/cost/PartitionScore.java +++ b/app/src/main/java/org/astraea/topic/cost/PartitionScore.java @@ -1,14 +1,15 @@ package org.astraea.topic.cost; -import static org.astraea.argument.ArgumentUtil.parseArgument; - import com.beust.jcommander.Parameter; -import com.beust.jcommander.converters.BooleanConverter; -import java.util.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import org.apache.kafka.common.TopicPartition; -import org.astraea.argument.BasicArgument; -import org.astraea.argument.validator.NotEmptyString; +import org.astraea.argument.BooleanField; import org.astraea.topic.TopicAdmin; public class PartitionScore { @@ -79,26 +80,26 @@ public static Map> execute( } public static void main(String[] args) { - var argument = parseArgument(new Argument(), args); + var argument = org.astraea.argument.Argument.parse(new Argument(), args); var admin = TopicAdmin.of(argument.brokers); var score = execute(argument, admin); printScore(score, argument); } - static class Argument extends BasicArgument { + static class Argument extends org.astraea.argument.Argument { @Parameter( names = {"--exclude.internal.topics"}, description = "True if you want to ignore internal topics like _consumer_offsets while counting score.", - validateWith = NotEmptyString.class, - converter = BooleanConverter.class) + validateWith = BooleanField.class, + converter = BooleanField.class) boolean excludeInternalTopic = false; @Parameter( names = {"--hide.balanced"}, description = "True if you want to hide topics and partitions thar already balanced.", - validateWith = NotEmptyString.class, - converter = BooleanConverter.class) + validateWith = BooleanField.class, + converter = BooleanField.class) boolean hideBalanced = false; } } diff --git a/app/src/main/java/org/astraea/utils/DataSize.java b/app/src/main/java/org/astraea/utils/DataSize.java index f37f1053d5..ac3f15fc12 100644 --- a/app/src/main/java/org/astraea/utils/DataSize.java +++ b/app/src/main/java/org/astraea/utils/DataSize.java @@ -1,6 +1,5 @@ package org.astraea.utils; -import com.beust.jcommander.IStringConverter; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; @@ -11,6 +10,7 @@ import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.astraea.argument.NonEmptyField; /** Data size class */ public class DataSize implements Comparable { @@ -215,7 +215,7 @@ public int hashCode() { return Objects.hash(bits); } - public static class Converter implements IStringConverter { + public static class Field implements NonEmptyField { // Parse number and DataUnit private static final Pattern DATA_SIZE_PATTERN = Pattern.compile("(?[0-9]+)\\s?(?[a-zA-Z]+)"); diff --git a/app/src/test/java/org/astraea/argument/ArgumentUtilTest.java b/app/src/test/java/org/astraea/argument/ArgumentUtilTest.java deleted file mode 100644 index 2a3ed8d017..0000000000 --- a/app/src/test/java/org/astraea/argument/ArgumentUtilTest.java +++ /dev/null @@ -1,178 +0,0 @@ -package org.astraea.argument; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import java.time.Duration; -import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Stream; -import org.astraea.argument.converter.DurationConverter; -import org.astraea.argument.converter.StringSetConverter; -import org.astraea.argument.validator.NonNegativeLong; -import org.astraea.argument.validator.NotEmptyString; -import org.astraea.argument.validator.PositiveLong; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.MethodSource; - -public class ArgumentUtilTest { - public static class FakeParameter { - @Parameter( - names = {"--require"}, - validateWith = NotEmptyString.class, - required = true) - public String require; - - @Parameter( - names = {"--longPositive"}, - validateWith = PositiveLong.class) - public long longPositive; - - @Parameter( - names = {"--longNotNegative"}, - validateWith = NonNegativeLong.class) - public int longNotNegative = 1; - - @Parameter( - names = {"--durationConvert"}, - converter = DurationConverter.class) - public Duration durationConvert; - - @Parameter( - names = {"--setConverter"}, - converter = StringSetConverter.class, - variableArity = true) - public Set setConverter; - } - - @Test - public void testParse() { - var param = - ArgumentUtil.parseArgument(new FakeParameter(), new String[] {"--require", "require"}); - Assertions.assertEquals("require", param.require); - } - - @Test - public void testRequired() { - Assertions.assertThrows( - ParameterException.class, - () -> ArgumentUtil.parseArgument(new FakeParameter(), new String[] {})); - } - - @Test - public void testLongPositive() { - var param = - ArgumentUtil.parseArgument( - new FakeParameter(), new String[] {"--require", "require", "--longPositive", "1000"}); - - Assertions.assertEquals(1000, param.longPositive); - Assertions.assertThrows( - ParameterException.class, - () -> - ArgumentUtil.parseArgument( - new FakeParameter(), new String[] {"--require", "require", "--longPositive", "0"})); - } - - @Test - public void testNotNegative() { - FakeParameter param = - ArgumentUtil.parseArgument( - new FakeParameter(), - new String[] {"--require", "require", "--longNotNegative", "1000"}); - - Assertions.assertEquals(1000, param.longNotNegative); - Assertions.assertThrows( - ParameterException.class, - () -> - ArgumentUtil.parseArgument( - new FakeParameter(), - new String[] {"--require", "require", "--longNotNegative", "-1"})); - } - - @Test - public void testDurationConvert() { - FakeParameter param = - ArgumentUtil.parseArgument( - new FakeParameter(), - new String[] {"--require", "require", "--durationConvert", "1000"}); - - Assertions.assertEquals(Duration.ofSeconds(1000), param.durationConvert); - } - - @Test - public void testSetConverter() { - FakeParameter param = - ArgumentUtil.parseArgument( - new FakeParameter(), - new String[] {"--require", "require", "--setConverter", "1", "1", "2"}); - - Assertions.assertEquals(Set.of("1", "2"), param.setConverter); - } - - private static Stream testDurationConvertorTestcases() { - return Stream.of( - Arguments.of("1", Duration.ofSeconds(1)), - Arguments.of("0", Duration.ZERO), - Arguments.of("60s", Duration.ofSeconds(60)), - Arguments.of("30m", Duration.ofMinutes(30)), - Arguments.of("24h", Duration.ofHours(24)), - Arguments.of("7day", Duration.ofDays(7)), - Arguments.of("7days", Duration.ofDays(7)), - Arguments.of("100ms", Duration.ofMillis(100)), - Arguments.of("500us", Duration.ofNanos(500 * 1000)), - Arguments.of("1ns", Duration.ofNanos(1))); - } - - @ParameterizedTest(name = "[{index}] time string \"{0}\" will match duration \"{1}\"") - @MethodSource("testDurationConvertorTestcases") - public void testDurationConvertorConvert(String timeString, Duration expectedDuration) { - var durationConverter = new DurationConverter(); - - Assertions.assertEquals(expectedDuration, durationConverter.convert(timeString)); - } - - @ParameterizedTest() - @CsvSource( - delimiterString = ",", - value = { - // input string, is_legal, test-purpose - " 0, true , valid unit", - " 1, true , valid unit", - " 5566, true , valid unit", - " 1234ns, true , valid unit", - " 4321us, true , valid unit", - " 1234ms, true , valid unit", - " 12000s, true , valid unit", - " 60m, true , valid unit", - " 60h, true , valid unit", - " 365day, true , valid unit", - " 365days, true , valid unit", - " 0010100days, true , valid unit", - " -1234, false , currently no negative number allowed", - " -1234ms, false , currently no negative number allowed", - " -365days, false , currently no negative number allowed", - " 0.5s, false , currently no floating value allowed", - " hello, false , illegal time/unit", - " ms, false , illegal time/unit", - " day, false , illegal time/unit", - " h, false , illegal time/unit", - }) - public void testDurationConvertorValidate(String timeString, boolean isLegal) { - var execution = - (Supplier) - () -> { - try { - var durationConverter = new DurationConverter(); - durationConverter.validate("key", timeString); - return true; - } catch (ParameterException ignored) { - return false; - } - }; - - Assertions.assertEquals(isLegal, execution.get()); - } -} diff --git a/app/src/test/java/org/astraea/argument/BasicArgumentTest.java b/app/src/test/java/org/astraea/argument/BasicArgumentTest.java index c97fa79836..f4e8ef7a2e 100644 --- a/app/src/test/java/org/astraea/argument/BasicArgumentTest.java +++ b/app/src/test/java/org/astraea/argument/BasicArgumentTest.java @@ -16,12 +16,13 @@ void testCommonProperties() throws IOException { output.write("key2=value2"); } var argument = - ArgumentUtil.parseArgument(new DumbArgument(), new String[] {"--bootstrap.servers", "abc"}); + org.astraea.argument.Argument.parse( + new DumbArgument(), new String[] {"--bootstrap.servers", "abc"}); Assertions.assertEquals(3, argument.properties(file.toString()).size()); Assertions.assertEquals("abc", argument.brokers); Assertions.assertEquals("value1", argument.properties(file.toString()).get("key1").toString()); Assertions.assertEquals("value2", argument.properties(file.toString()).get("key2").toString()); } - private static class DumbArgument extends BasicArgument {} + private static class DumbArgument extends Argument {} } diff --git a/app/src/test/java/org/astraea/argument/CompressionFieldTest.java b/app/src/test/java/org/astraea/argument/CompressionFieldTest.java new file mode 100644 index 0000000000..be6c69e4c0 --- /dev/null +++ b/app/src/test/java/org/astraea/argument/CompressionFieldTest.java @@ -0,0 +1,34 @@ +package org.astraea.argument; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import java.util.stream.Stream; +import org.apache.kafka.common.record.CompressionType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CompressionFieldTest { + + private static class FakeParameter { + @Parameter( + names = {"--field"}, + converter = CompressionField.class, + validateWith = CompressionField.class) + public CompressionType value; + } + + @Test + void testConversion() { + var arg = new CompressionField(); + Stream.of(CompressionType.values()) + .forEach(type -> Assertions.assertEquals(type, arg.convert(type.name))); + Assertions.assertThrows(ParameterException.class, () -> arg.convert("aaa")); + } + + @Test + void testParse() { + var arg = + org.astraea.argument.Argument.parse(new FakeParameter(), new String[] {"--field", "gzip"}); + Assertions.assertEquals(CompressionType.GZIP, arg.value); + } +} diff --git a/app/src/test/java/org/astraea/argument/DurationFieldTest.java b/app/src/test/java/org/astraea/argument/DurationFieldTest.java new file mode 100644 index 0000000000..1082746c7e --- /dev/null +++ b/app/src/test/java/org/astraea/argument/DurationFieldTest.java @@ -0,0 +1,77 @@ +package org.astraea.argument; + +import com.beust.jcommander.ParameterException; +import java.time.Duration; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +public class DurationFieldTest { + @ParameterizedTest() + @CsvSource( + delimiterString = ",", + value = { + // input string, is_legal, test-purpose + " 0, true , valid unit", + " 1, true , valid unit", + " 5566, true , valid unit", + " 1234ns, true , valid unit", + " 4321us, true , valid unit", + " 1234ms, true , valid unit", + " 12000s, true , valid unit", + " 60m, true , valid unit", + " 60h, true , valid unit", + " 365day, true , valid unit", + " 365days, true , valid unit", + " 0010100days, true , valid unit", + " -1234, false , currently no negative number allowed", + " -1234ms, false , currently no negative number allowed", + " -365days, false , currently no negative number allowed", + " 0.5s, false , currently no floating value allowed", + " hello, false , illegal time/unit", + " ms, false , illegal time/unit", + " day, false , illegal time/unit", + " h, false , illegal time/unit", + }) + public void testDurationConvertorValidate(String timeString, boolean isLegal) { + var execution = + (Supplier) + () -> { + try { + var durationConverter = new DurationField(); + durationConverter.validate("key", timeString); + return true; + } catch (ParameterException ignored) { + return false; + } + }; + + Assertions.assertEquals(isLegal, execution.get()); + } + + @ParameterizedTest(name = "[{index}] time string \"{0}\" will match duration \"{1}\"") + @MethodSource("testDurationConvertorTestcases") + public void testDurationConvertorConvert(String timeString, Duration expectedDuration) { + var durationConverter = new DurationField(); + + Assertions.assertEquals(expectedDuration, durationConverter.convert(timeString)); + } + + private static Stream testDurationConvertorTestcases() { + return Stream.of( + Arguments.of("1", Duration.ofSeconds(1)), + Arguments.of("0", Duration.ZERO), + Arguments.of("60s", Duration.ofSeconds(60)), + Arguments.of("30m", Duration.ofMinutes(30)), + Arguments.of("24h", Duration.ofHours(24)), + Arguments.of("7day", Duration.ofDays(7)), + Arguments.of("7days", Duration.ofDays(7)), + Arguments.of("100ms", Duration.ofMillis(100)), + Arguments.of("500us", Duration.ofNanos(500 * 1000)), + Arguments.of("1ns", Duration.ofNanos(1))); + } +} diff --git a/app/src/test/java/org/astraea/argument/NonNegativeFieldTest.java b/app/src/test/java/org/astraea/argument/NonNegativeFieldTest.java new file mode 100644 index 0000000000..5ef681d116 --- /dev/null +++ b/app/src/test/java/org/astraea/argument/NonNegativeFieldTest.java @@ -0,0 +1,29 @@ +package org.astraea.argument; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NonNegativeFieldTest { + + private static class FakeParameter { + @Parameter( + names = {"--field"}, + validateWith = NonNegativeLongField.class) + public int value = 1; + } + + @Test + public void testNotNegative() { + var param = + org.astraea.argument.Argument.parse(new FakeParameter(), new String[] {"--field", "1000"}); + + Assertions.assertEquals(1000, param.value); + Assertions.assertThrows( + ParameterException.class, + () -> + org.astraea.argument.Argument.parse( + new FakeParameter(), new String[] {"--field", "-1"})); + } +} diff --git a/app/src/test/java/org/astraea/argument/PositiveFieldTest.java b/app/src/test/java/org/astraea/argument/PositiveFieldTest.java new file mode 100644 index 0000000000..9408d02ba0 --- /dev/null +++ b/app/src/test/java/org/astraea/argument/PositiveFieldTest.java @@ -0,0 +1,34 @@ +package org.astraea.argument; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PositiveFieldTest { + + private static class FakeParameter { + @Parameter( + names = {"--field"}, + validateWith = PositiveLongField.class) + public long value = 1; + } + + @Test + public void testNotNegative() { + var param = + org.astraea.argument.Argument.parse(new FakeParameter(), new String[] {"--field", "1000"}); + + Assertions.assertEquals(1000, param.value); + Assertions.assertThrows( + ParameterException.class, + () -> + org.astraea.argument.Argument.parse( + new FakeParameter(), new String[] {"--field", "0"})); + Assertions.assertThrows( + ParameterException.class, + () -> + org.astraea.argument.Argument.parse( + new FakeParameter(), new String[] {"--field", "-1"})); + } +} diff --git a/app/src/test/java/org/astraea/argument/SetFieldTest.java b/app/src/test/java/org/astraea/argument/SetFieldTest.java new file mode 100644 index 0000000000..911719a745 --- /dev/null +++ b/app/src/test/java/org/astraea/argument/SetFieldTest.java @@ -0,0 +1,24 @@ +package org.astraea.argument; + +import com.beust.jcommander.Parameter; +import java.util.Set; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class SetFieldTest { + private static class FakeParameter { + @Parameter( + names = {"--field"}, + converter = StringSetField.class, + variableArity = true) + public Set value; + } + + @Test + public void testSetConverter() { + var param = + org.astraea.argument.Argument.parse(new FakeParameter(), new String[] {"--field", "1,2,3"}); + + Assertions.assertEquals(Set.of("1", "2", "3"), param.value); + } +} diff --git a/app/src/test/java/org/astraea/metrics/MetricExplorerTest.java b/app/src/test/java/org/astraea/metrics/MetricExplorerTest.java index 7aa324c682..ca04b1d525 100644 --- a/app/src/test/java/org/astraea/metrics/MetricExplorerTest.java +++ b/app/src/test/java/org/astraea/metrics/MetricExplorerTest.java @@ -10,7 +10,6 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.astraea.argument.ArgumentUtil; import org.astraea.metrics.jmx.BeanObject; import org.astraea.metrics.jmx.MBeanClient; import org.junit.jupiter.api.function.Executable; @@ -33,7 +32,7 @@ class MetricExplorerTest { }) void testPatternJmxUrlStart(String url, String expected) { // arrange - Pattern pattern = Argument.JmxServerUrlConverter.patternOfJmxUrlStart; + Pattern pattern = Argument.JmxServerUrlField.patternOfJmxUrlStart; // act Matcher matcher = pattern.matcher(url); @@ -73,7 +72,7 @@ void testPatternProperty(String property, String key, String value) { }) void executeDoesPrintSomething(String args) { // arrange - var argument = ArgumentUtil.parseArgument(new Argument(), args.split(" ")); + var argument = org.astraea.argument.Argument.parse(new Argument(), args.split(" ")); var mockMBeanClient = mock(MBeanClient.class); when(mockMBeanClient.queryBeans(any())) .thenReturn(List.of(new BeanObject("example.com", Map.of("key", "value"), Map.of()))); @@ -111,7 +110,7 @@ void ensureArgumentWorking(String argumentString, String outcome) { String[] arguments = argumentString.split(" "); // act - Executable doParsing = () -> ArgumentUtil.parseArgument(new Argument(), arguments); + Executable doParsing = () -> org.astraea.argument.Argument.parse(new Argument(), arguments); // assert if (outcome.equals("ok")) { diff --git a/app/src/test/java/org/astraea/performance/CompressionArgumentTest.java b/app/src/test/java/org/astraea/performance/CompressionArgumentTest.java deleted file mode 100644 index 30d96a5479..0000000000 --- a/app/src/test/java/org/astraea/performance/CompressionArgumentTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.astraea.performance; - -import com.beust.jcommander.ParameterException; -import java.util.stream.Stream; -import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.common.record.CompressionType; -import org.astraea.argument.ArgumentUtil; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class CompressionArgumentTest { - - @Test - void testConversion() { - var arg = new Performance.CompressionArgument(); - Stream.of(CompressionType.values()) - .forEach(type -> Assertions.assertEquals(type, arg.convert(type.name))); - Assertions.assertThrows(ParameterException.class, () -> arg.convert("aaa")); - } - - @Test - void testParse() { - var arg = - ArgumentUtil.parseArgument( - new Performance.Argument(), - new String[] {"--bootstrap.servers", "aa", "--compression", "gzip"}); - Assertions.assertEquals(CompressionType.GZIP, arg.compression); - Assertions.assertEquals( - "gzip", arg.producerProps().get(ProducerConfig.COMPRESSION_TYPE_CONFIG)); - Assertions.assertNull(arg.props().get(ProducerConfig.COMPRESSION_TYPE_CONFIG)); - } - - @Test - void testJmxServer() { - var arg = - ArgumentUtil.parseArgument( - new Performance.Argument(), - new String[] {"--bootstrap.servers", "aa", "--jmx.servers", "aaa"}); - Assertions.assertEquals("aaa", arg.producerProps().get("jmx_servers")); - } -} diff --git a/app/src/test/java/org/astraea/performance/ExeTimeTest.java b/app/src/test/java/org/astraea/performance/ExeTimeTest.java index 04cd1e0e7f..1cf7bb0c0d 100644 --- a/app/src/test/java/org/astraea/performance/ExeTimeTest.java +++ b/app/src/test/java/org/astraea/performance/ExeTimeTest.java @@ -5,34 +5,30 @@ import org.junit.jupiter.api.Test; public class ExeTimeTest { - private final ExeTime.Converter converter = new ExeTime.Converter(); - private final ExeTime.Validator validator = new ExeTime.Validator(); + private final ExeTime.Field field = new ExeTime.Field(); @Test void testRecords() { - Assertions.assertDoesNotThrow(() -> validator.validate("--run.until", "1000records")); - var exeTime = converter.convert("1000records"); + Assertions.assertDoesNotThrow(() -> field.validate("--run.until", "1000records")); + var exeTime = field.convert("1000records"); Assertions.assertEquals(0, exeTime.percentage(0, 10)); Assertions.assertEquals(100D, exeTime.percentage(1000, 10)); } @Test void testDuration() { - Assertions.assertDoesNotThrow(() -> validator.validate("--run.until", "100ms")); - var exeTime = converter.convert("100ms"); + Assertions.assertDoesNotThrow(() -> field.validate("--run.until", "100ms")); + var exeTime = field.convert("100ms"); Assertions.assertEquals(0, exeTime.percentage(1000, 0)); Assertions.assertEquals(100D, exeTime.percentage(1000, 100)); } @Test void testUnknownArgument() { + Assertions.assertThrows(ParameterException.class, () -> field.validate("--run.until", "aaa")); Assertions.assertThrows( - ParameterException.class, () -> validator.validate("--run.until", "aaa")); - Assertions.assertThrows( - ParameterException.class, () -> validator.validate("--run.until", "10record")); - Assertions.assertThrows( - ParameterException.class, () -> validator.validate("--run.until", "1000")); - Assertions.assertThrows( - ParameterException.class, () -> validator.validate("--run.until", "10Ms")); + ParameterException.class, () -> field.validate("--run.until", "10record")); + Assertions.assertThrows(ParameterException.class, () -> field.validate("--run.until", "1000")); + Assertions.assertThrows(ParameterException.class, () -> field.validate("--run.until", "10Ms")); } } diff --git a/app/src/test/java/org/astraea/topic/ReplicaSyncingMonitorIntegrationTest.java b/app/src/test/java/org/astraea/topic/ReplicaSyncingMonitorIntegrationTest.java index a70e7e9b14..6b4c6e031e 100644 --- a/app/src/test/java/org/astraea/topic/ReplicaSyncingMonitorIntegrationTest.java +++ b/app/src/test/java/org/astraea/topic/ReplicaSyncingMonitorIntegrationTest.java @@ -8,7 +8,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import org.apache.kafka.common.TopicPartition; -import org.astraea.argument.ArgumentUtil; import org.astraea.producer.Producer; import org.astraea.producer.Sender; import org.astraea.service.RequireBrokerCluster; @@ -53,7 +52,7 @@ void execute() throws IOException, InterruptedException { topicAdmin.migrator().partition(TOPIC_NAME, 0).moveTo(Set.of(moveToBroker)); ReplicaSyncingMonitor.execute( topicAdmin, - ArgumentUtil.parseArgument( + org.astraea.argument.Argument.parse( new ReplicaSyncingMonitor.Argument(), new String[] { "--bootstrap.servers", diff --git a/app/src/test/java/org/astraea/topic/ReplicaSyncingMonitorTest.java b/app/src/test/java/org/astraea/topic/ReplicaSyncingMonitorTest.java index c238a7570d..8b9aee44f6 100644 --- a/app/src/test/java/org/astraea/topic/ReplicaSyncingMonitorTest.java +++ b/app/src/test/java/org/astraea/topic/ReplicaSyncingMonitorTest.java @@ -17,7 +17,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.kafka.common.TopicPartition; -import org.astraea.argument.ArgumentUtil; import org.astraea.utils.DataUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -86,7 +85,7 @@ void execute() throws InterruptedException { () -> { ReplicaSyncingMonitor.execute( mockTopicAdmin, - ArgumentUtil.parseArgument( + org.astraea.argument.Argument.parse( new ReplicaSyncingMonitor.Argument(), new String[] { "--bootstrap.servers", "whatever:9092", "--interval", interval + "ms" @@ -159,7 +158,7 @@ void executeWithKeepTrack() throws InterruptedException { try { ReplicaSyncingMonitor.execute( mockTopicAdmin, - ArgumentUtil.parseArgument( + org.astraea.argument.Argument.parse( new ReplicaSyncingMonitor.Argument(), new String[] { "--bootstrap.servers", @@ -211,7 +210,7 @@ void executeWithTopic() { try { ReplicaSyncingMonitor.execute( mockTopicAdmin, - ArgumentUtil.parseArgument( + org.astraea.argument.Argument.parse( new ReplicaSyncingMonitor.Argument(), new String[] { "--bootstrap.servers", @@ -282,7 +281,8 @@ void ensureArgumentFlagExists() { // act Consumer execution = - (String[] args) -> ArgumentUtil.parseArgument(new ReplicaSyncingMonitor.Argument(), args); + (String[] args) -> + org.astraea.argument.Argument.parse(new ReplicaSyncingMonitor.Argument(), args); // assert correct.stream() diff --git a/app/src/test/java/org/astraea/utils/DataSizeTest.java b/app/src/test/java/org/astraea/utils/DataSizeTest.java index 5a21cfdfd3..e9df2fdb66 100644 --- a/app/src/test/java/org/astraea/utils/DataSizeTest.java +++ b/app/src/test/java/org/astraea/utils/DataSizeTest.java @@ -148,7 +148,7 @@ void of(String unitName, String expectedBits) { @Test void parseDataSize() { - var converter = new DataSize.Converter(); + var converter = new DataSize.Field(); assertEquals(DataUnit.Bit.of(100).toString(), converter.convert("100Bit").toString()); assertEquals(DataUnit.Kb.of(100).toString(), converter.convert("100 Kb").toString()); assertEquals(DataUnit.Mb.of(100).toString(), converter.convert("100 Mb").toString());