Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion assemble/bin/accumulo
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,18 @@ function main() {
JAVA=("${ACCUMULO_JAVA_PREFIX[@]}" "$JAVA")
fi

exec "${JAVA[@]}" "${JAVA_OPTS[@]}" org.apache.accumulo.start.Main "$@"
# Allow users to supply extra Accumulo arguments via ACCUMULO_MAIN_ARGS
if ! declare -p ACCUMULO_MAIN_ARGS &>/dev/null; then
ACCUMULO_MAIN_ARGS=()
else
declare_output=$(declare -p ACCUMULO_MAIN_ARGS 2>/dev/null)
if [[ $declare_output != declare\ -a* ]]; then
ACCUMULO_MAIN_ARGS_RAW=${ACCUMULO_MAIN_ARGS[*]}
read -r -a ACCUMULO_MAIN_ARGS <<<"${ACCUMULO_MAIN_ARGS_RAW}"
fi
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious why this is handled differently than how JAVA_OPTS is handled, is there a reason?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I have a couple of thoughts on this:

  1. This is making me really regret that I didn't push harder against the -o property passing. My original proposal when we reworked the launch scripts was to support Java system properties for accumulo configs, as in: -Daccumulo.rpc.bind.port=9999. I don't know the reason Mike implemented the -o, but it is really making it inconvenient here. If we could just pass them as system properties, then this would be a non-issue. And, I still think that's the better implementation.
  2. The default value should be a range that servers of any type can share. So, most of the time, users don't need to specify anything in their config files.
  3. If one is going to specify properties in config files, we don't need any of this, because we can inject properties into the commons-configuration file using property interpolation. If a user wants to set a custom port, they can either use a shared accumulo.properties file that has an entry like rpc.bind.port=${env.RPC_PORT} and then do export RPC_PORT=9999 in accumulo-env.sh based on the server type, or they can use something like rpc.bind.port=${sys:accumulo.rpc.bind.port} and set -Daccumulo.rpc.bind.port=9999 in their JAVA_OPTS based on the server type, OR they can import server-type-specific properties using includes that pull in a specific config for a specific server type, and they can spread their config over multiple config files, OR they can choose different wholly independent config files, by adding -Daccumulo.properties=/path/to/server-specific.properties in their accumulo-env.sh based on the server type.

So, we just don't need this. We already have better options than making the scripts more complicated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding point # 3, it would be useful to have an example of how the includes would work. If you understand commons-configuration and how it works, then it's obvious. If you don't, then it's not obvious and the user is unlikely to even think about it as a viable option.


exec "${JAVA[@]}" "${JAVA_OPTS[@]}" org.apache.accumulo.start.Main "$@" "${ACCUMULO_MAIN_ARGS[@]}"
}

main "$@"
10 changes: 10 additions & 0 deletions assemble/conf/accumulo-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,13 @@ esac
## environment, that will override what is set here, rather than some mangled
## merged result. You can set the variable any way you like.
#declare -p 'ACCUMULO_JAVA_PREFIX' &>/dev/null || ACCUMULO_JAVA_PREFIX=''

Comment thread
DomGarguilo marked this conversation as resolved.
Outdated
## ACCUMULO_MAIN_ARGS can be used to pass extra arguments directly to
## org.apache.accumulo.start.Main (after the JVM options). Declare as an array
## to avoid issues with spaces in values.
#case "$cmd" in
# monitor) ACCUMULO_MAIN_ARGS=(-o "rpc.bind.port=9995") ;;
# tserver) ACCUMULO_MAIN_ARGS=(-o "rpc.bind.port=20000-20049") ;;
# compactor) ACCUMULO_MAIN_ARGS=(-o "rpc.bind.port=20050-20099") ;;
# sserver) ACCUMULO_MAIN_ARGS=(-o "rpc.bind.port=20100-20149") ;;
Comment thread
DomGarguilo marked this conversation as resolved.
Outdated
#esac
Original file line number Diff line number Diff line change
Expand Up @@ -347,21 +347,27 @@ public IntStream getPortStream(Property property) {
checkType(property, PropertyType.PORT);

String portString = get(property);
try {
Preconditions.checkArgument(portString != null, "Port cannot be null.");
portString = portString.trim();
Preconditions.checkArgument(!portString.isEmpty(), "Port cannot be empty.");
Comment thread
DomGarguilo marked this conversation as resolved.
Outdated

if (portString.contains("-")) {
// value is a range, parse it as such
return PortRange.parse(portString);
} catch (IllegalArgumentException e) {
try {
int port = Integer.parseInt(portString);
if (port == 0 || PortRange.VALID_RANGE.contains(port)) {
return IntStream.of(port);
} else {
log.error("Invalid port number {}; Using default {}", port, property.getDefaultValue());
return IntStream.of(Integer.parseInt(property.getDefaultValue()));
}
} catch (NumberFormatException e1) {
throw new IllegalArgumentException("Invalid port syntax. Must be a single positive "
+ "integers or a range (M-N) of positive integers");
}

// getting to this point means the value is a single port (not a range)
try {
int port = Integer.parseInt(portString);
if (port == 0 || PortRange.VALID_RANGE.contains(port)) {
return IntStream.of(port);
}
log.error("Invalid port number: {}. Using default instead: {}", port,
property.getDefaultValue());
return PortRange.parse(property.getDefaultValue().trim());
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"Invalid port syntax. Must be a single positive integer or a range M-N.", e);
}
}

Expand Down
66 changes: 26 additions & 40 deletions core/src/main/java/org/apache/accumulo/core/conf/Property.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ public enum Property {
The local IP address to which this server should bind for sending \
and receiving network traffic. If not set then the process binds to all addresses.
""", "2.1.4"),
RPC_BIND_PORT("rpc.bind.port", "19000-19999", PropertyType.PORT,
"""
The port or range of ports servers attempt to bind for RPC traffic. Provide a single \
value to target an exact port (will attempt higher ports if given port is already in use, \
up to 1000 additional checks), or a range using formats like '19000-19999' to allow searching for \
the first available port within that range.
""",
"4.0.0"),
Comment on lines +98 to +105
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will attempt higher ports if given port is already in use, up to 1000 additional checks

I don't think we should do this. If the user specifies a single port, then we should honor it. If they want to use port search, then they should specify a range.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. This was one of the things in the original ticket:

A single integer value, A, should be interpreted as the range [A, A+1), and implies no port searching.... use that one value or fail.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d6d8553. Only ports in the given range will be used.

RPC_MAX_MESSAGE_SIZE("rpc.message.size.max", Integer.toString(Integer.MAX_VALUE),
PropertyType.BYTES, "The maximum size of a message that can be received by a server.",
"2.1.3"),
Expand Down Expand Up @@ -395,8 +403,6 @@ was changed and it now can accept multiple class names. The metrics spi was intr
// properties that are specific to manager server behavior
MANAGER_PREFIX("manager.", null, PropertyType.PREFIX,
"Properties in this category affect the behavior of the manager server.", "2.1.0"),
MANAGER_CLIENTPORT("manager.port.client", "9999", PropertyType.PORT,
"The port used for handling client connections on the manager.", "1.3.5"),
Comment on lines -402 to -403
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably a good idea to keep these around and deprecated. If the new property is set, then we should use the new behavior and ignore anything in the old properties. Otherwise, we should read the old properties and use them.

Ideally, we'd introduce this in 3.1 and deprecate them there, so we don't need to have these in 4.0 at all.

Because this is a much simpler config, that hopefully users just don't have to worry about much because the defaults are reasonable, I don't think it would be completely terrible if we just changed this without a deprecation, but we would definitely need to call that out in the release notes.

MANAGER_TABLET_BALANCER("manager.tablet.balancer",
"org.apache.accumulo.core.spi.balancer.TableLoadBalancer", PropertyType.CLASSNAME,
"The balancer class that accumulo will use to make tablet assignment and "
Expand Down Expand Up @@ -585,11 +591,6 @@ Each key is the name of the pool (can be assigned any string). Each value is a J
expiration time, will trigger a background refresh for future hits. \
Value must be less than 100%. Set to 0 will disable refresh.
""", "2.1.3"),
SSERV_PORTSEARCH("sserver.port.search", "true", PropertyType.BOOLEAN,
"if the sserver.port.client ports are in use, search higher ports until one is available.",
"2.1.0"),
SSERV_CLIENTPORT("sserver.port.client", "9996", PropertyType.PORT,
"The port used for handling client connections on the tablet servers.", "2.1.0"),
SSERV_MINTHREADS("sserver.server.threads.minimum", "20", PropertyType.COUNT,
"The minimum number of threads to use to handle incoming requests.", "2.1.0"),
SSERV_MINTHREADS_TIMEOUT("sserver.server.threads.timeout", "0s", PropertyType.TIMEDURATION,
Expand Down Expand Up @@ -637,11 +638,6 @@ Each key is the name of the pool (can be assigned any string). Each value is a J
"Specifies the size of the cache for RFile index blocks.", "1.3.5"),
TSERV_SUMMARYCACHE_SIZE("tserver.cache.summary.size", "10%", PropertyType.MEMORY,
"Specifies the size of the cache for summary data on each tablet server.", "2.0.0"),
TSERV_PORTSEARCH("tserver.port.search", "true", PropertyType.BOOLEAN,
"if the tserver.port.client ports are in use, search higher ports until one is available.",
"1.3.5"),
TSERV_CLIENTPORT("tserver.port.client", "9997", PropertyType.PORT,
"The port used for handling client connections on the tablet servers.", "1.3.5"),
TSERV_TOTAL_MUTATION_QUEUE_MAX("tserver.total.mutation.queue.max", "5%", PropertyType.MEMORY,
"The amount of memory used to store write-ahead-log mutations before flushing them.",
"1.7.0"),
Expand Down Expand Up @@ -846,8 +842,6 @@ Each key is the name of the pool (can be assigned any string). Each value is a J
"Time between garbage collection cycles. In each cycle, old RFiles or write-ahead logs "
+ "no longer in use are removed from the filesystem.",
"1.3.5"),
GC_PORT("gc.port.client", "9998", PropertyType.PORT,
"The listening port for the garbage collector's monitor service.", "1.3.5"),
GC_DELETE_WAL_THREADS("gc.threads.delete.wal", "4", PropertyType.COUNT,
"The number of threads used to delete write-ahead logs and recovery files.", "2.1.4"),
GC_DELETE_THREADS("gc.threads.delete", "16", PropertyType.COUNT,
Expand All @@ -864,8 +858,6 @@ Each key is the name of the pool (can be assigned any string). Each value is a J
// properties that are specific to the monitor server behavior
MONITOR_PREFIX("monitor.", null, PropertyType.PREFIX,
"Properties in this category affect the behavior of the monitor web server.", "1.3.5"),
MONITOR_PORT("monitor.port.client", "9995", PropertyType.PORT,
"The listening port for the monitor's http service.", "1.3.5"),
MONITOR_SSL_KEYSTORE("monitor.ssl.keyStore", "", PropertyType.PATH,
"The keystore for enabling monitor SSL.", "1.5.0"),
@Sensitive
Expand Down Expand Up @@ -1285,11 +1277,6 @@ start with the category prefix, followed by a scope (minc, majc, scan, \
+ " should be cancelled. This checks for situations like was the tablet deleted (split "
+ " and merge do this), was the table deleted, was a user compaction canceled, etc.",
"2.1.4"),
COMPACTOR_PORTSEARCH("compactor.port.search", "true", PropertyType.BOOLEAN,
"If the compactor.port.client ports are in use, search higher ports until one is available.",
"2.1.0"),
COMPACTOR_CLIENTPORT("compactor.port.client", "9133", PropertyType.PORT,
"The port used for handling client connections on the compactor servers.", "2.1.0"),
COMPACTOR_MIN_JOB_WAIT_TIME("compactor.wait.time.job.min", "1s", PropertyType.TIMEDURATION,
"The minimum amount of time to wait between checks for the next compaction job, backing off"
+ "exponentially until COMPACTOR_MAX_JOB_WAIT_TIME is reached.",
Expand Down Expand Up @@ -1626,6 +1613,7 @@ public static boolean isValidTablePropertyKey(String key) {
// RPC options
RPC_BACKLOG, RPC_SSL_KEYSTORE_TYPE, RPC_SSL_TRUSTSTORE_TYPE, RPC_USE_JSSE,
RPC_SSL_ENABLED_PROTOCOLS, RPC_SSL_CLIENT_PROTOCOL, RPC_SASL_QOP, RPC_MAX_MESSAGE_SIZE,
RPC_BIND_PORT,

// INSTANCE options
INSTANCE_ZK_HOST, INSTANCE_ZK_TIMEOUT, INSTANCE_SECRET, INSTANCE_SECURITY_AUTHENTICATOR,
Expand All @@ -1644,40 +1632,38 @@ public static boolean isValidTablePropertyKey(String key) {
// MANAGER options
MANAGER_THREADCHECK, MANAGER_FATE_METRICS_MIN_UPDATE_INTERVAL, MANAGER_METADATA_SUSPENDABLE,
MANAGER_STARTUP_TSERVER_AVAIL_MIN_COUNT, MANAGER_STARTUP_TSERVER_AVAIL_MAX_WAIT,
MANAGER_CLIENTPORT, MANAGER_MINTHREADS, MANAGER_MINTHREADS_TIMEOUT,
MANAGER_RECOVERY_WAL_EXISTENCE_CACHE_TIME, MANAGER_COMPACTION_SERVICE_PRIORITY_QUEUE_SIZE,
MANAGER_TABLET_REFRESH_MINTHREADS, MANAGER_TABLET_REFRESH_MAXTHREADS,
MANAGER_TABLET_MERGEABILITY_INTERVAL, MANAGER_FATE_CONDITIONAL_WRITER_THREADS_MAX,
MANAGER_MINTHREADS, MANAGER_MINTHREADS_TIMEOUT, MANAGER_RECOVERY_WAL_EXISTENCE_CACHE_TIME,
MANAGER_COMPACTION_SERVICE_PRIORITY_QUEUE_SIZE, MANAGER_TABLET_REFRESH_MINTHREADS,
MANAGER_TABLET_REFRESH_MAXTHREADS, MANAGER_TABLET_MERGEABILITY_INTERVAL,
MANAGER_FATE_CONDITIONAL_WRITER_THREADS_MAX,

// SSERV options
SSERV_CACHED_TABLET_METADATA_REFRESH_PERCENT, SSERV_THREADCHECK, SSERV_CLIENTPORT,
SSERV_PORTSEARCH, SSERV_DATACACHE_SIZE, SSERV_INDEXCACHE_SIZE, SSERV_SUMMARYCACHE_SIZE,
SSERV_DEFAULT_BLOCKSIZE, SSERV_SCAN_REFERENCE_EXPIRATION_TIME,
SSERV_CACHED_TABLET_METADATA_EXPIRATION, SSERV_MINTHREADS, SSERV_MINTHREADS_TIMEOUT,
SSERV_WAL_SORT_MAX_CONCURRENT, SSERV_GROUP_NAME,
SSERV_CACHED_TABLET_METADATA_REFRESH_PERCENT, SSERV_THREADCHECK, SSERV_DATACACHE_SIZE,
SSERV_INDEXCACHE_SIZE, SSERV_SUMMARYCACHE_SIZE, SSERV_DEFAULT_BLOCKSIZE,
SSERV_SCAN_REFERENCE_EXPIRATION_TIME, SSERV_CACHED_TABLET_METADATA_EXPIRATION,
SSERV_MINTHREADS, SSERV_MINTHREADS_TIMEOUT, SSERV_WAL_SORT_MAX_CONCURRENT, SSERV_GROUP_NAME,

// TSERV options
TSERV_TOTAL_MUTATION_QUEUE_MAX, TSERV_WAL_MAX_SIZE, TSERV_WAL_MAX_AGE,
TSERV_WAL_TOLERATED_CREATION_FAILURES, TSERV_WAL_TOLERATED_WAIT_INCREMENT,
TSERV_WAL_TOLERATED_MAXIMUM_WAIT_DURATION, TSERV_MAX_IDLE, TSERV_SESSION_MAXIDLE,
TSERV_SCAN_RESULTS_MAX_TIMEOUT, TSERV_MINC_MAXCONCURRENT, TSERV_THREADCHECK,
TSERV_LOG_BUSY_TABLETS_COUNT, TSERV_LOG_BUSY_TABLETS_INTERVAL, TSERV_WAL_SORT_MAX_CONCURRENT,
TSERV_SLOW_FILEPERMIT_MILLIS, TSERV_WAL_BLOCKSIZE, TSERV_CLIENTPORT, TSERV_PORTSEARCH,
TSERV_DATACACHE_SIZE, TSERV_INDEXCACHE_SIZE, TSERV_SUMMARYCACHE_SIZE, TSERV_DEFAULT_BLOCKSIZE,
TSERV_MINTHREADS, TSERV_MINTHREADS_TIMEOUT, TSERV_NATIVEMAP_ENABLED, TSERV_MAXMEM,
TSERV_SCAN_MAX_OPENFILES, TSERV_ONDEMAND_UNLOADER_INTERVAL, TSERV_GROUP_NAME,
TSERV_SLOW_FILEPERMIT_MILLIS, TSERV_WAL_BLOCKSIZE, TSERV_DATACACHE_SIZE,
TSERV_INDEXCACHE_SIZE, TSERV_SUMMARYCACHE_SIZE, TSERV_DEFAULT_BLOCKSIZE, TSERV_MINTHREADS,
TSERV_MINTHREADS_TIMEOUT, TSERV_NATIVEMAP_ENABLED, TSERV_MAXMEM, TSERV_SCAN_MAX_OPENFILES,
TSERV_ONDEMAND_UNLOADER_INTERVAL, TSERV_GROUP_NAME,

// GC options
GC_CANDIDATE_BATCH_SIZE, GC_CYCLE_START, GC_PORT,
GC_CANDIDATE_BATCH_SIZE, GC_CYCLE_START,

// MONITOR options
MONITOR_PORT, MONITOR_SSL_KEYSTORETYPE, MONITOR_SSL_TRUSTSTORETYPE,
MONITOR_SSL_INCLUDE_PROTOCOLS, MONITOR_LOCK_CHECK_INTERVAL, MONITOR_ROOT_CONTEXT,
MONITOR_SSL_KEYSTORETYPE, MONITOR_SSL_TRUSTSTORETYPE, MONITOR_SSL_INCLUDE_PROTOCOLS,
MONITOR_LOCK_CHECK_INTERVAL, MONITOR_ROOT_CONTEXT,

// COMPACTOR options
COMPACTOR_CANCEL_CHECK_INTERVAL, COMPACTOR_CLIENTPORT, COMPACTOR_THREADCHECK,
COMPACTOR_PORTSEARCH, COMPACTOR_MINTHREADS, COMPACTOR_MINTHREADS_TIMEOUT,
COMPACTOR_GROUP_NAME,
COMPACTOR_CANCEL_CHECK_INTERVAL, COMPACTOR_THREADCHECK, COMPACTOR_MINTHREADS,
COMPACTOR_MINTHREADS_TIMEOUT, COMPACTOR_GROUP_NAME,

// COMPACTION_COORDINATOR options
COMPACTION_COORDINATOR_DEAD_COMPACTOR_CHECK_INTERVAL,
Expand Down
91 changes: 58 additions & 33 deletions core/src/main/java/org/apache/accumulo/core/conf/PropertyType.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.apache.accumulo.core.fate.Fate;
import org.apache.accumulo.core.file.rfile.RFile;
Expand Down Expand Up @@ -96,13 +95,11 @@ public enum PropertyType {
+ " 'localhost:2000,www.example.com,10.10.1.1:500' and 'localhost'.\n"
+ "Examples of invalid host lists are '', ':1000', and 'localhost:80000'"),

PORT("port",
x -> Stream.of(new Bounds(1024, 65535), in(true, "0"), new PortRange("\\d{4,5}-\\d{4,5}"))
.anyMatch(y -> y.test(x)),
"An positive integer in the range 1024-65535 (not already in use or"
+ " specified elsewhere in the configuration),\n"
+ "zero to indicate any open ephemeral port, or a range of positive"
+ " integers specified as M-N"),
PORT("port", new PortPredicate(),
"A positive integer in the range 1024-65535 (not already in use or specified elsewhere in the"
+ " configuration), zero to indicate any open ephemeral port, or a range of positive"
+ " integers expressed as M-N (inclusive) or using interval notation such as [M,N) or"
+ " [M,N]."),

COUNT("count", new Bounds(0, Integer.MAX_VALUE),
"A non-negative integer in the range of 0-" + Integer.MAX_VALUE),
Expand Down Expand Up @@ -423,43 +420,71 @@ public boolean test(final String input) {

}

public static class PortRange extends Matches {

public static final Range<Integer> VALID_RANGE = Range.of(1024, 65535);

public PortRange(final String pattern) {
super(pattern);
}
private static class PortPredicate implements Predicate<String> {

@Override
public boolean test(final String input) {
if (super.test(input)) {
if (input == null) {
return true;
}
final String trimmed = input.trim();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're trimming for validation, we need to trim when we use it. It might make sense here, but I think it would be novel. Looking at the code, we only trim the input in PropertyType in two other places, and one might be a bug.

  1. We trim when we strip the units off of the end of bounded units, so we can isolate the numeric part, because we're only testing the number part.
  2. We trim absolute paths during validation (might be a bug... seems sketchy at a glance)

In general, we assume that the property value is exactly what the user specified, including any spaces. I don't know that we should always do that, but it would be nice if we were consistent, and it doesn't look like we do anything to transform the user's input by stripping off whitespace in most of the other property types, so we probably shouldn't here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the use of .trim in 7375e2d. Added a return false to PortPredicate() if the value contains any whitespace in that commit too. That will cause PropertyType.PORT.isValidFormat() to return false before the whitespace causes issues further down the line.

if (trimmed.isEmpty()) {
return false;
}
if ("0".equals(trimmed)) {
return true;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is 0 valid here? It seems like it shouldn't be.

Copy link
Copy Markdown
Member Author

@DomGarguilo DomGarguilo Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several existing places in the code where we set "0" as the value for the port:

private void mergePropWithRandomPort(String key) {
if (!siteConfig.containsKey(key)) {
siteConfig.put(key, "0");
}
}

This is used for all the
mergePropWithRandomPort(Property.MANAGER_CLIENTPORT.getKey());
mergePropWithRandomPort(Property.TSERV_CLIENTPORT.getKey());
mergePropWithRandomPort(Property.MONITOR_PORT.getKey());
mergePropWithRandomPort(Property.GC_PORT.getKey());

It looks like eventually the prop gets used in the server builder code to create a new InetSocketAddress(host, port) whose javadoc says "A valid port value is between 0 and 65535. A port number of zero will let the system pick up an ephemeral port in a bind operation."

It would be a change in functionality to disallow "0".

There are several other places as well as tests and ITs that set 0 for the port. Maybe we could document this better.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to the docs for RPC_BIND_PORT in d6d8553


try {
int port = Integer.parseInt(trimmed);
return PortRange.VALID_RANGE.contains(port);
} catch (NumberFormatException e) {
try {
PortRange.parse(input);
PortRange.parse(trimmed);
return true;
} catch (IllegalArgumentException e) {
} catch (IllegalArgumentException ex) {
return false;
}
} else {
return false;
}
}
}

public static IntStream parse(String portRange) {
int idx = portRange.indexOf('-');
if (idx != -1) {
int low = Integer.parseInt(portRange.substring(0, idx));
int high = Integer.parseInt(portRange.substring(idx + 1));
if (!VALID_RANGE.contains(low) || !VALID_RANGE.contains(high) || low > high) {
throw new IllegalArgumentException(
"Invalid port range specified, only 1024 to 65535 supported.");
}
return IntStream.rangeClosed(low, high);
public static class PortRange {

public static final Range<Integer> VALID_RANGE = Range.of(1024, 65535);

private PortRange() {}

public static IntStream parse(String value) {
Objects.requireNonNull(value, "Port range cannot be null.");
value = value.trim();
Preconditions.checkArgument(!value.isEmpty(), "Port range cannot be empty.");
Preconditions.checkArgument(value.contains("-"),
"Invalid port range, expected format like M-N.");
String[] parts = value.split("-", 2);
Preconditions.checkArgument(parts.length == 2, "Invalid port range, must use M-N notation.");

int low;
int high;
try {
low = Integer.parseInt(parts[0].trim());
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid port value: " + parts[0], e);
}
throw new IllegalArgumentException(
"Invalid port range specification, must use M-N notation.");
}
try {
high = Integer.parseInt(parts[1].trim());
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid port value: " + parts[1], e);
}

Preconditions.checkArgument(VALID_RANGE.contains(low),
"Port range bounds must be 1024 to 65535. Got " + low);
Preconditions.checkArgument(VALID_RANGE.contains(high),
"Port range bounds must be 1024 to 65535 Got " + high);
Preconditions.checkArgument(high > low, "Upper bound must be >= lower bound.");

return IntStream.rangeClosed(low, high);
}
}

private static class ValidFateConfig implements Predicate<String> {
Expand Down
Loading