Skip to content

Commit

Permalink
Reduce object allocations for assertions #1068
Browse files Browse the repository at this point in the history
LettuceAssert now accepts message suppliers to defer message creation so e.g. String.format is invoked lazily.
  • Loading branch information
mp911de committed Jun 26, 2019
1 parent 5c91bae commit 6b0752b
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 22 deletions.
12 changes: 6 additions & 6 deletions src/main/java/io/lettuce/core/RedisURI.java
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ public static Builder redis(String host) {
public static Builder redis(String host, int port) {

LettuceAssert.notEmpty(host, "Host must not be empty");
LettuceAssert.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
LettuceAssert.isTrue(isValidPort(port), () -> String.format("Port out of range: %s", port));

Builder builder = RedisURI.builder();
return builder.withHost(host).withPort(port);
Expand Down Expand Up @@ -1061,7 +1061,7 @@ public static Builder sentinel(String host) {
public static Builder sentinel(String host, int port) {

LettuceAssert.notEmpty(host, "Host must not be empty");
LettuceAssert.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
LettuceAssert.isTrue(isValidPort(port), () -> String.format("Port out of range: %s", port));

Builder builder = RedisURI.builder();
return builder.withSentinel(host, port);
Expand Down Expand Up @@ -1102,7 +1102,7 @@ public static Builder sentinel(String host, int port, String masterId) {
public static Builder sentinel(String host, int port, String masterId, CharSequence password) {

LettuceAssert.notEmpty(host, "Host must not be empty");
LettuceAssert.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
LettuceAssert.isTrue(isValidPort(port), () -> String.format("Port out of range: %s", port));

Builder builder = RedisURI.builder();
if (password != null) {
Expand Down Expand Up @@ -1150,7 +1150,7 @@ public Builder withSentinel(String host, int port, CharSequence password) {

LettuceAssert.assertState(this.host == null, "Cannot use with Redis mode.");
LettuceAssert.notEmpty(host, "Host must not be empty");
LettuceAssert.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
LettuceAssert.isTrue(isValidPort(port), () -> String.format("Port out of range: %s", port));

RedisURI redisURI = RedisURI.create(host, port);

Expand Down Expand Up @@ -1200,7 +1200,7 @@ public Builder withHost(String host) {
public Builder withPort(int port) {

LettuceAssert.assertState(this.host != null, "Host is null. Cannot use in Sentinel mode.");
LettuceAssert.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
LettuceAssert.isTrue(isValidPort(port), () -> String.format("Port out of range: %s", port));

this.port = port;
return this;
Expand Down Expand Up @@ -1253,7 +1253,7 @@ public Builder withVerifyPeer(boolean verifyPeer) {
*/
public Builder withDatabase(int database) {

LettuceAssert.isTrue(database >= 0, "Invalid database number: " + database);
LettuceAssert.isTrue(database >= 0, () -> "Invalid database number: " + database);

this.database = database;
return this;
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/io/lettuce/core/SslOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ public Builder keystore(File keystore) {
public Builder keystore(File keystore, char[] keystorePassword) {

LettuceAssert.notNull(keystore, "Keystore must not be null");
LettuceAssert.isTrue(keystore.exists(), String.format("Keystore file %s does not exist", truststore));
LettuceAssert.isTrue(keystore.isFile(), String.format("Keystore file %s is not a file", truststore));
LettuceAssert.isTrue(keystore.exists(), () -> String.format("Keystore file %s does not exist", truststore));
LettuceAssert.isTrue(keystore.isFile(), () -> String.format("Keystore file %s is not a file", truststore));

try {
return keystore(keystore.toURI().toURL(), keystorePassword);
Expand Down Expand Up @@ -230,8 +230,8 @@ public Builder truststore(File truststore) {
public Builder truststore(File truststore, String truststorePassword) {

LettuceAssert.notNull(truststore, "Truststore must not be null");
LettuceAssert.isTrue(truststore.exists(), String.format("Truststore file %s does not exist", truststore));
LettuceAssert.isTrue(truststore.isFile(), String.format("Truststore file %s is not a file", truststore));
LettuceAssert.isTrue(truststore.exists(), () -> String.format("Truststore file %s does not exist", truststore));
LettuceAssert.isTrue(truststore.isFile(), () -> String.format("Truststore file %s is not a file", truststore));

try {
return truststore(truststore.toURI().toURL(), truststorePassword);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public AsyncExecutableCommandLookupStrategy(List<RedisCodec<?, ?>> redisCodecs,
public ExecutableCommand resolveCommandMethod(CommandMethod method, RedisCommandsMetadata metadata) {

LettuceAssert.isTrue(!method.isReactiveExecution(),
String.format("Command method %s not supported by this command lookup strategy", method));
() -> String.format("Command method %s not supported by this command lookup strategy", method));

CommandFactory commandFactory = super.resolveCommandFactory(method, metadata);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private static boolean isForceFlush(CommandMethod method) {
public ExecutableCommand resolveCommandMethod(CommandMethod method, RedisCommandsMetadata metadata) {

LettuceAssert.isTrue(!method.isReactiveExecution(),
String.format("Command method %s not supported by this command lookup strategy", method));
() -> String.format("Command method %s not supported by this command lookup strategy", method));

ExecutionSpecificParameters parameters = (ExecutionSpecificParameters) method.getParameters();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ class ReactiveExecutableCommandLookupStrategy implements ExecutableCommandLookup
public ExecutableCommand resolveCommandMethod(CommandMethod method, RedisCommandsMetadata commandsMetadata) {

LettuceAssert.isTrue(!method.isBatchExecution(),
String.format("Command batching %s not supported with ReactiveExecutableCommandLookupStrategy", method));
() -> String.format("Command batching %s not supported with ReactiveExecutableCommandLookupStrategy", method));

LettuceAssert.isTrue(method.isReactiveExecution(),
String.format("Command method %s not supported by ReactiveExecutableCommandLookupStrategy", method));
() -> String.format("Command method %s not supported by ReactiveExecutableCommandLookupStrategy", method));

ReactiveCommandSegmentCommandFactory commandFactory = commandFactoryResolver.resolveRedisCommandFactory(method,
commandsMetadata);
Expand Down
14 changes: 7 additions & 7 deletions src/main/java/io/lettuce/core/internal/HostAndPort.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ private HostAndPort(String hostText, int port) {
*/
public static HostAndPort of(String host, int port) {

LettuceAssert.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
LettuceAssert.isTrue(isValidPort(port), () -> String.format("Port out of range: %s", port));

HostAndPort parsedHost = parse(host);
LettuceAssert.isTrue(!parsedHost.hasPort(), String.format("Host has a port: %s", host));
LettuceAssert.isTrue(!parsedHost.hasPort(), () -> String.format("Host has a port: %s", host));
return new HostAndPort(host, port);
}

Expand Down Expand Up @@ -92,13 +92,13 @@ public static HostAndPort parse(String hostPortString) {
if (!LettuceStrings.isEmpty(portString)) {
// Try to parse the whole port string as a number.
// JDK7 accepts leading plus signs. We don't want to.
LettuceAssert.isTrue(!portString.startsWith("+"), String.format("Cannot port number: %s", hostPortString));
LettuceAssert.isTrue(!portString.startsWith("+"), () -> String.format("Cannot port number: %s", hostPortString));
try {
port = Integer.parseInt(portString);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(String.format("Cannot parse port number: %s", hostPortString));
}
LettuceAssert.isTrue(isValidPort(port), String.format("Port number out of range: %s", hostPortString));
LettuceAssert.isTrue(isValidPort(port), () -> String.format("Port number out of range: %s", hostPortString));
}

return new HostAndPort(host, port);
Expand Down Expand Up @@ -187,13 +187,13 @@ public int hashCode() {
private static String[] getHostAndPortFromBracketedHost(String hostPortString) {

LettuceAssert.isTrue(hostPortString.charAt(0) == '[',
String.format("Bracketed host-port string must start with a bracket: %s", hostPortString));
() -> String.format("Bracketed host-port string must start with a bracket: %s", hostPortString));

int colonIndex = hostPortString.indexOf(':');
int closeBracketIndex = hostPortString.lastIndexOf(']');

LettuceAssert.isTrue(colonIndex > -1 && closeBracketIndex > colonIndex,
String.format("Invalid bracketed host/port: %s", hostPortString));
() -> String.format("Invalid bracketed host/port: %s", hostPortString));

String host = hostPortString.substring(1, closeBracketIndex);
if (closeBracketIndex + 1 == hostPortString.length()) {
Expand All @@ -204,7 +204,7 @@ private static String[] getHostAndPortFromBracketedHost(String hostPortString) {
"Only a colon may follow a close bracket: " + hostPortString);
for (int i = closeBracketIndex + 2; i < hostPortString.length(); ++i) {
LettuceAssert.isTrue(Character.isDigit(hostPortString.charAt(i)),
String.format("Port must be numeric: %s", hostPortString));
() -> String.format("Port must be numeric: %s", hostPortString));
}
return new String[] { host, hostPortString.substring(closeBracketIndex + 2) };
}
Expand Down
108 changes: 108 additions & 0 deletions src/main/java/io/lettuce/core/internal/LettuceAssert.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.lettuce.core.internal;

import java.util.Collection;
import java.util.function.Supplier;

import io.lettuce.core.LettuceStrings;

Expand Down Expand Up @@ -46,6 +47,20 @@ public static void notEmpty(String string, String message) {
}
}

/**
* Assert that a string is not empty, it must not be {@code null} and it must not be empty.
*
* @param string the object to check
* @param messageSupplier the exception message supplier to use if the assertion fails
* @throws IllegalArgumentException if the object is {@code null} or the underlying string is empty
* @since 5.2.0
*/
public static void notEmpty(String string, Supplier<String> messageSupplier) {
if (LettuceStrings.isEmpty(string)) {
throw new IllegalArgumentException(messageSupplier.get());
}
}

/**
* Assert that an object is not {@code null} .
*
Expand All @@ -59,6 +74,20 @@ public static void notNull(Object object, String message) {
}
}

/**
* Assert that an object is not {@code null} .
*
* @param object the object to check
* @param messageSupplier the exception message supplier to use if the assertion fails
* @throws IllegalArgumentException if the object is {@code null}
* @since 5.2.0
*/
public static void notNull(Object object, Supplier<String> messageSupplier) {
if (object == null) {
throw new IllegalArgumentException(messageSupplier.get());
}
}

/**
* Assert that an array has elements; that is, it must not be {@code null} and must have at least one element.
*
Expand All @@ -72,6 +101,20 @@ public static void notEmpty(Object[] array, String message) {
}
}

/**
* Assert that an array has elements; that is, it must not be {@code null} and must have at least one element.
*
* @param array the array to check
* @param messageSupplier the exception message supplier to use if the assertion fails
* @throws IllegalArgumentException if the object array is {@code null} or has no elements
* @since 5.2.0
*/
public static void notEmpty(Object[] array, Supplier<String> messageSupplier) {
if (array == null || array.length == 0) {
throw new IllegalArgumentException(messageSupplier.get());
}
}

/**
* Assert that an array has elements; that is, it must not be {@code null} and must have at least one element.
*
Expand Down Expand Up @@ -102,6 +145,24 @@ public static void noNullElements(Object[] array, String message) {
}
}

/**
* Assert that an array has no null elements.
*
* @param array the array to check
* @param messageSupplier the exception message supplier to use if the assertion fails
* @throws IllegalArgumentException if the object array contains a {@code null} element
* @since 5.2.0
*/
public static void noNullElements(Object[] array, Supplier<String> messageSupplier) {
if (array != null) {
for (Object element : array) {
if (element == null) {
throw new IllegalArgumentException(messageSupplier.get());
}
}
}
}

/**
* Assert that a {@link java.util.Collection} has no null elements.
*
Expand All @@ -119,6 +180,24 @@ public static void noNullElements(Collection<?> c, String message) {
}
}

/**
* Assert that a {@link java.util.Collection} has no null elements.
*
* @param c the collection to check
* @param messageSupplier the exception message supplier to use if the assertion fails
* @throws IllegalArgumentException if the {@link Collection} contains a {@code null} element
* @since 5.2.0
*/
public static void noNullElements(Collection<?> c, Supplier<String> messageSupplier) {
if (c != null) {
for (Object element : c) {
if (element == null) {
throw new IllegalArgumentException(messageSupplier.get());
}
}
}
}

/**
* Assert that {@code value} is {@literal true}.
*
Expand All @@ -132,6 +211,20 @@ public static void isTrue(boolean value, String message) {
}
}

/**
* Assert that {@code value} is {@literal true}.
*
* @param value the value to check
* @param messageSupplier the exception message supplier to use if the assertion fails
* @throws IllegalArgumentException if the object array contains a {@code null} element
* @since 5.2.0
*/
public static void isTrue(boolean value, Supplier<String> messageSupplier) {
if (!value) {
throw new IllegalArgumentException(messageSupplier.get());
}
}

/**
* Ensures the truth of an expression involving the state of the calling instance, but not involving any parameters to the
* calling method.
Expand All @@ -145,4 +238,19 @@ public static void assertState(boolean condition, String message) {
throw new IllegalStateException(message);
}
}

/**
* Ensures the truth of an expression involving the state of the calling instance, but not involving any parameters to the
* calling method.
*
* @param condition a boolean expression
* @param messageSupplier the exception message supplier to use if the assertion fails
* @throws IllegalStateException if {@code expression} is false
* @since 5.2.0
*/
public static void assertState(boolean condition, Supplier<String> messageSupplier) {
if (!condition) {
throw new IllegalStateException(messageSupplier.get());
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/io/lettuce/core/models/role/RoleParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private RoleParser() {
public static RedisInstance parse(List<?> roleOutput) {
LettuceAssert.isTrue(roleOutput != null && !roleOutput.isEmpty(), "Empty role output");
LettuceAssert.isTrue(roleOutput.get(0) instanceof String && ROLE_MAPPING.containsKey(roleOutput.get(0)),
"First role element must be a string (any of " + ROLE_MAPPING.keySet() + ")");
() -> "First role element must be a string (any of " + ROLE_MAPPING.keySet() + ")");

RedisInstance.Role role = ROLE_MAPPING.get(roleOutput.get(0));

Expand Down

0 comments on commit 6b0752b

Please sign in to comment.