com.rabbitmq.stream.perf.Utils Maven / Gradle / Ivy
// Copyright (c) 2020-2021 VMware, Inc. or its affiliates. All rights reserved.
//
// This software, the RabbitMQ Stream Java client library, is dual-licensed under the
// Mozilla Public License 2.0 ("MPL"), and the Apache License version 2 ("ASL").
// For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL,
// please see LICENSE-APACHE2.
//
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
// either express or implied. See the LICENSE file for specific language governing
// rights and limitations of this software.
//
// If you have any questions regarding licensing, please contact us at
// [email protected].
package com.rabbitmq.stream.perf;
import com.rabbitmq.stream.ByteCapacity;
import com.rabbitmq.stream.OffsetSpecification;
import com.rabbitmq.stream.StreamCreator.LeaderLocator;
import com.rabbitmq.stream.compression.Compression;
import java.security.cert.X509Certificate;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.net.ssl.X509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
class Utils {
static final X509TrustManager TRUST_EVERYTHING_TRUST_MANAGER = new TrustEverythingTrustManager();
private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
private static final String RANGE_SEPARATOR_1 = "-";
private static final String RANGE_SEPARATOR_2 = "..";
static void writeLong(byte[] array, long value) {
// from Guava Longs
for (int i = 7; i >= 0; i--) {
array[i] = (byte) (value & 0xffL);
value >>= 8;
}
}
static long readLong(byte[] array) {
// from Guava Longs
return (array[0] & 0xFFL) << 56
| (array[1] & 0xFFL) << 48
| (array[2] & 0xFFL) << 40
| (array[3] & 0xFFL) << 32
| (array[4] & 0xFFL) << 24
| (array[5] & 0xFFL) << 16
| (array[6] & 0xFFL) << 8
| (array[7] & 0xFFL);
}
static List streams(String range, List streams) {
if (range.contains(RANGE_SEPARATOR_2)) {
range = range.replace(RANGE_SEPARATOR_2, RANGE_SEPARATOR_1);
}
int from, to;
if (range.contains(RANGE_SEPARATOR_1)) {
String[] fromTo = range.split(RANGE_SEPARATOR_1);
from = Integer.parseInt(fromTo[0]);
to = Integer.parseInt(fromTo[1]) + 1;
} else {
int count = Integer.parseInt(range);
from = 1;
to = count + 1;
}
if (from == 1 && to == 2) {
return streams;
} else {
if (streams.size() != 1) {
throw new IllegalArgumentException("Enter only 1 stream when --stream-count is specified");
}
String format = streams.get(0);
String streamFormat;
if (!format.contains("%")) {
int digits = String.valueOf(to - 1).length();
streamFormat = format + "-%0" + digits + "d";
} else {
streamFormat = format;
}
return IntStream.range(from, to)
.mapToObj(i -> String.format(streamFormat, i))
.collect(Collectors.toList());
}
}
static String formatByte(double bytes) {
// based on
// https://stackoverflow.com/questions/3758606/how-can-i-convert-byte-size-into-a-human-readable-format-in-java
if (-1000 < bytes && bytes < 1000) {
return String.valueOf(bytes);
}
CharacterIterator ci = new StringCharacterIterator("kMGTPE");
while (bytes <= -999_950 || bytes >= 999_950) {
bytes /= 1000;
ci.next();
}
return String.format("%.1f %cB", bytes / 1000.0, ci.current());
}
static long physicalMemory() {
try {
com.sun.management.OperatingSystemMXBean os =
(com.sun.management.OperatingSystemMXBean)
java.lang.management.ManagementFactory.getOperatingSystemMXBean();
return os.getTotalPhysicalMemorySize();
} catch (Throwable e) {
// we can get NoClassDefFoundError, so we catch from Throwable and below
LOGGER.warn("Could not get physical memory", e);
return 0;
}
}
private static void throwConversionException(String format, String... arguments) {
throw new CommandLine.TypeConversionException(String.format(format, (Object[]) arguments));
}
static class ByteCapacityTypeConverter implements CommandLine.ITypeConverter {
@Override
public ByteCapacity convert(String value) {
try {
return ByteCapacity.from(value);
} catch (IllegalArgumentException e) {
throw new CommandLine.TypeConversionException(
"'" + value + "' is not valid, valid example values: 100gb, 50mb");
}
}
}
static class ConsumerNameStrategyConverter
implements CommandLine.ITypeConverter> {
@Override
public BiFunction convert(String input) {
if ("uuid".equals(input)) {
return (stream, index) -> UUID.randomUUID().toString();
} else {
return new PatternConsumerNameStrategy(input);
}
}
}
static class RangeTypeConverter implements CommandLine.ITypeConverter {
@Override
public String convert(String input) {
String value;
if (input.contains(RANGE_SEPARATOR_2)) {
value = input.replace(RANGE_SEPARATOR_2, RANGE_SEPARATOR_1);
} else {
value = input;
}
if (value.contains(RANGE_SEPARATOR_1)) {
String[] fromTo = value.split(RANGE_SEPARATOR_1);
if (fromTo == null || fromTo.length != 2) {
throwConversionException("'%s' is not valid, valid examples values: 10, 1-10", input);
}
Arrays.stream(fromTo)
.forEach(
v -> {
try {
int i = Integer.parseInt(v);
if (i <= 0) {
throwConversionException(
"'%s' is not valid, the value must be a positive integer", v);
}
} catch (NumberFormatException e) {
throwConversionException(
"'%s' is not valid, the value must be a positive integer", v);
}
});
int from = Integer.parseInt(fromTo[0]);
int to = Integer.parseInt(fromTo[1]);
if (from >= to) {
throwConversionException("'%s' is not valid, valid examples values: 10, 1-10", input);
}
} else {
try {
int count = Integer.parseInt(value);
if (count <= 0) {
throwConversionException(
"'%s' is not valid, the value must be a positive integer", input);
}
} catch (NumberFormatException e) {
throwConversionException("'%s' is not valid, valid example values: 10, 1-10", input);
}
}
return input;
}
}
static class DurationTypeConverter implements CommandLine.ITypeConverter {
@Override
public Duration convert(String value) {
try {
Duration duration = Duration.parse(value);
if (duration.isNegative() || duration.isZero()) {
throw new CommandLine.TypeConversionException(
"'" + value + "' is not valid, it must be positive");
}
return duration;
} catch (DateTimeParseException e) {
throw new CommandLine.TypeConversionException(
"'" + value + "' is not valid, valid example values: PT15M, PT10H");
}
}
}
static class LeaderLocatorTypeConverter implements CommandLine.ITypeConverter {
@Override
public LeaderLocator convert(String value) {
try {
return LeaderLocator.from(value);
} catch (Exception e) {
throw new CommandLine.TypeConversionException(
"'"
+ value
+ "' is not valid, possible values: "
+ Arrays.stream(LeaderLocator.values())
.map(ll -> ll.value())
.collect(Collectors.joining(", ")));
}
}
}
static class OffsetSpecificationTypeConverter
implements CommandLine.ITypeConverter {
private static final Map SPECS =
Collections.unmodifiableMap(
new HashMap() {
{
put("first", OffsetSpecification.first());
put("last", OffsetSpecification.last());
put("next", OffsetSpecification.next());
}
});
@Override
public OffsetSpecification convert(String value) throws Exception {
if (value == null || value.trim().isEmpty()) {
return OffsetSpecification.first();
}
if (SPECS.containsKey(value.toLowerCase())) {
return SPECS.get(value.toLowerCase());
}
try {
long offset = Long.parseUnsignedLong(value);
return OffsetSpecification.offset(offset);
} catch (NumberFormatException e) {
// trying next
}
try {
TemporalAccessor accessor = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(value);
return OffsetSpecification.timestamp(Instant.from(accessor).toEpochMilli());
} catch (DateTimeParseException e) {
throw new CommandLine.TypeConversionException(
"'"
+ value
+ "' is not a valid offset value, valid values are 'first', 'last', 'next', "
+ "an unsigned long, or an ISO 8601 formatted timestamp (eg. 2020-06-03T07:45:54Z)");
}
}
}
static class PositiveIntegerTypeConverter implements CommandLine.ITypeConverter {
@Override
public Integer convert(String input) {
try {
Integer value = Integer.valueOf(input);
if (value <= 0) {
throw new IllegalArgumentException();
}
return value;
} catch (Exception e) {
throw new CommandLine.TypeConversionException(input + " is not a positive integer");
}
}
}
static class CompressionTypeConverter implements CommandLine.ITypeConverter {
@Override
public Compression convert(String input) {
try {
return Compression.valueOf(input.toUpperCase(Locale.ENGLISH));
} catch (Exception e) {
throw new CommandLine.TypeConversionException(
input
+ " is not a valid compression value. "
+ "Accepted values are "
+ Arrays.stream(Compression.values())
.map(Compression::name)
.map(String::toLowerCase)
.collect(Collectors.joining(", "))
+ ".");
}
}
}
private abstract static class RangeIntegerTypeConverter
implements CommandLine.ITypeConverter {
private final int min, max;
private RangeIntegerTypeConverter(int min, int max) {
this.min = min;
this.max = max;
}
@Override
public Integer convert(String input) {
try {
Integer value = Integer.valueOf(input);
if (value < this.min || value > this.max) {
throw new IllegalArgumentException();
}
return value;
} catch (Exception e) {
throw new CommandLine.TypeConversionException(
input + " must an integer between " + this.min + " and " + this.max);
}
}
}
static class OneTo255RangeIntegerTypeConverter extends RangeIntegerTypeConverter {
OneTo255RangeIntegerTypeConverter() {
super(1, 255);
}
}
static class NotNegativeIntegerTypeConverter implements CommandLine.ITypeConverter {
@Override
public Integer convert(String input) {
try {
Integer value = Integer.valueOf(input);
if (value < 0) {
throw new IllegalArgumentException();
}
return value;
} catch (Exception e) {
throw new CommandLine.TypeConversionException(input + " is not a non-negative integer");
}
}
}
static class NamedThreadFactory implements ThreadFactory {
private final ThreadFactory backingThreaFactory;
private final String prefix;
private final AtomicLong count = new AtomicLong(0);
public NamedThreadFactory(String prefix) {
this(Executors.defaultThreadFactory(), prefix);
}
public NamedThreadFactory(ThreadFactory backingThreaFactory, String prefix) {
this.backingThreaFactory = backingThreaFactory;
this.prefix = prefix;
}
@Override
public Thread newThread(Runnable r) {
Thread thread = this.backingThreaFactory.newThread(r);
thread.setName(prefix + count.getAndIncrement());
return thread;
}
}
private static class TrustEverythingTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
static final class PatternConsumerNameStrategy implements BiFunction {
private final String pattern;
PatternConsumerNameStrategy(String pattern) {
this.pattern = pattern;
}
@Override
public String apply(String stream, Integer index) {
return String.format(pattern, stream, index);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy