diff --git a/src/main/java/com/lambdaworks/redis/RedisCommandBuilder.java b/src/main/java/com/lambdaworks/redis/RedisCommandBuilder.java index 6271472133..e8f74a3db7 100644 --- a/src/main/java/com/lambdaworks/redis/RedisCommandBuilder.java +++ b/src/main/java/com/lambdaworks/redis/RedisCommandBuilder.java @@ -21,6 +21,10 @@ */ class RedisCommandBuilder extends BaseRedisCommandBuilder { + private static final String MUST_NOT_CONTAIN_NULL_ELEMENTS = "must not contain null elements"; + private static final String MUST_NOT_BE_EMPTY = "must not be empty"; + private static final String MUST_NOT_BE_NULL = "must not be null"; + public RedisCommandBuilder(RedisCodec codec) { super(codec); } @@ -30,6 +34,8 @@ public Command append(K key, V value) { } public Command auth(String password) { + assertNotEmpty(password, "password " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).add(password); return createCommand(AUTH, new StatusOutput(codec), args); } @@ -66,6 +72,8 @@ public Command bitpos(K key, boolean state, long start, long end) { } public Command bitopAnd(K destination, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec); args.add(AND).addKey(destination).addKeys(keys); return createCommand(BITOP, new IntegerOutput(codec), args); @@ -78,23 +86,31 @@ public Command bitopNot(K destination, K source) { } public Command bitopOr(K destination, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec); args.add(OR).addKey(destination).addKeys(keys); return createCommand(BITOP, new IntegerOutput(codec), args); } public Command bitopXor(K destination, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec); args.add(XOR).addKey(destination).addKeys(keys); return createCommand(BITOP, new IntegerOutput(codec), args); } public Command> blpop(long timeout, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys).add(timeout); return createCommand(BLPOP, new KeyValueOutput(codec), args); } public Command> brpop(long timeout, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys).add(timeout); return createCommand(BRPOP, new KeyValueOutput(codec), args); } @@ -116,11 +132,15 @@ public Command clientSetname(K name) { } public Command clientKill(String addr) { + assertNotEmpty(addr, "addr " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).add(KILL).add(addr); return createCommand(CLIENT, new StatusOutput(codec), args); } public Command clientKill(KillArgs killArgs) { + assertNotNull(killArgs, "killArgs " + MUST_NOT_BE_NULL); + CommandArgs args = new CommandArgs(codec).add(KILL); killArgs.build(args); return createCommand(CLIENT, new IntegerOutput(codec), args); @@ -142,6 +162,10 @@ public Command> command() { } public Command> commandInfo(String... commands) { + + assertNotEmpty(commands, "commands " + MUST_NOT_BE_EMPTY); + assertNoNullElements(commands, "commands " + MUST_NOT_CONTAIN_NULL_ELEMENTS); + CommandArgs args = new CommandArgs(codec); args.add(INFO); @@ -206,6 +230,8 @@ public Command decrby(K key, long amount) { } public Command del(K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(DEL, new IntegerOutput(codec), args); } @@ -225,6 +251,8 @@ public Command echo(V msg) { } public Command eval(String script, ScriptOutputType type, K... keys) { + assertNotEmpty(script, "script " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec); args.add(script).add(keys.length).addKeys(keys); CommandOutput output = newScriptOutput(codec, type); @@ -232,6 +260,8 @@ public Command eval(String script, ScriptOutputType type, K... keys } public Command eval(String script, ScriptOutputType type, K[] keys, V... values) { + assertNotEmpty(script, "script " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec); args.add(script).add(keys.length).addKeys(keys).addValues(values); CommandOutput output = newScriptOutput(codec, type); @@ -239,6 +269,8 @@ public Command eval(String script, ScriptOutputType type, K[] keys, } public Command evalsha(String digest, ScriptOutputType type, K... keys) { + assertNotEmpty(digest, "digest " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec); args.add(digest).add(keys.length).addKeys(keys); CommandOutput output = newScriptOutput(codec, type); @@ -246,6 +278,8 @@ public Command evalsha(String digest, ScriptOutputType type, K... k } public Command evalsha(String digest, ScriptOutputType type, K[] keys, V... values) { + assertNotEmpty(digest, "digest " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec); args.add(digest).add(keys.length).addKeys(keys).addValues(values); CommandOutput output = newScriptOutput(codec, type); @@ -293,6 +327,8 @@ public Command getset(K key, V value) { } public Command hdel(K key, K... fields) { + assertNotEmpty(fields, "fields " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(fields); return createCommand(HDEL, new IntegerOutput(codec), args); } @@ -338,11 +374,15 @@ public Command hlen(K key) { } public Command> hmget(K key, K... fields) { + assertNotEmpty(fields, "fields " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(fields); return createCommand(HMGET, new ValueListOutput(codec), args); } public Command hmget(ValueStreamingChannel channel, K key, K... fields) { + assertNotEmpty(fields, "fields " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(fields); return createCommand(HMGET, new ValueStreamingOutput(codec, channel), args); } @@ -425,6 +465,8 @@ public Command lpop(K key) { } public Command lpush(K key, V... values) { + assertNotEmpty(values, "values " + MUST_NOT_BE_EMPTY); + return createCommand(LPUSH, new IntegerOutput(codec), key, values); } @@ -464,11 +506,15 @@ public Command migrate(String host, int port, K key, int db, long } public Command> mget(K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(MGET, new ValueListOutput(codec), args); } public Command mget(ValueStreamingChannel channel, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(MGET, new ValueStreamingOutput(codec, channel), args); } @@ -524,6 +570,7 @@ public Command pexpireat(K key, long timestamp) { public Command ping() { return createCommand(PING, new StatusOutput(codec)); } + public Command readOnly() { return createCommand(READONLY, new StatusOutput(codec)); } @@ -532,7 +579,6 @@ public Command readWrite() { return createCommand(READWRITE, new StatusOutput(codec)); } - public Command pttl(K key) { CommandArgs args = new CommandArgs(codec).addKey(key); return createCommand(PTTL, new IntegerOutput(codec), args); @@ -555,6 +601,8 @@ public Command> pubsubChannels(K pattern) { @SuppressWarnings({ "unchecked", "rawtypes" }) public Command> pubsubNumsub(K... pattern) { + assertNotEmpty(pattern, "pattern " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).add(NUMSUB).addKeys(pattern); return createCommand(PUBSUB, (MapOutput) new MapOutput((RedisCodec) codec), args); } @@ -587,6 +635,7 @@ public Command renamenx(K key, K newKey) { } public Command restore(K key, long ttl, byte[] value) { + CommandArgs args = new CommandArgs(codec).addKey(key).add(ttl).add(value); return createCommand(RESTORE, new StatusOutput(codec), args); } @@ -601,6 +650,8 @@ public Command rpoplpush(K source, K destination) { } public Command rpush(K key, V... values) { + assertNotEmpty(values, "values " + MUST_NOT_BE_EMPTY); + return createCommand(RPUSH, new IntegerOutput(codec), key, values); } @@ -609,6 +660,8 @@ public Command rpushx(K key, V value) { } public Command sadd(K key, V... members) { + assertNotEmpty(members, "members " + MUST_NOT_BE_EMPTY); + return createCommand(SADD, new IntegerOutput(codec), key, members); } @@ -621,6 +674,9 @@ public Command scard(K key) { } public Command> scriptExists(String... digests) { + assertNotEmpty(digests, "digests " + MUST_NOT_BE_EMPTY); + assertNoNullElements(digests, "digests " + MUST_NOT_CONTAIN_NULL_ELEMENTS); + CommandArgs args = new CommandArgs(codec).add(EXISTS); for (String sha : digests) { args.add(sha); @@ -644,16 +700,22 @@ public Command scriptLoad(V script) { } public Command> sdiff(K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(SDIFF, new ValueSetOutput(codec), args); } public Command sdiff(ValueStreamingChannel channel, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(SDIFF, new ValueStreamingOutput(codec, channel), args); } public Command sdiffstore(K destination, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKey(destination).addKeys(keys); return createCommand(SDIFFSTORE, new IntegerOutput(codec), args); } @@ -708,16 +770,22 @@ public Command shutdown(boolean save) { } public Command> sinter(K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(SINTER, new ValueSetOutput(codec), args); } public Command sinter(ValueStreamingChannel channel, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(SINTER, new ValueStreamingOutput(codec, channel), args); } public Command sinterstore(K destination, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKey(destination).addKeys(keys); return createCommand(SINTERSTORE, new IntegerOutput(codec), args); } @@ -732,6 +800,8 @@ public Command smove(K source, K destination, V member) { } public Command slaveof(String host, int port) { + assertNotEmpty(host, "host " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).add(host).add(port); return createCommand(SLAVEOF, new StatusOutput(codec), args); } @@ -814,20 +884,28 @@ public Command srandmember(ValueStreamingChannel channel, K key, } public Command srem(K key, V... members) { + assertNotEmpty(members, "members " + MUST_NOT_BE_EMPTY); + return createCommand(SREM, new IntegerOutput(codec), key, members); } public Command> sunion(K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(SUNION, new ValueSetOutput(codec), args); } public Command sunion(ValueStreamingChannel channel, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(SUNION, new ValueStreamingOutput(codec, channel), args); } public Command sunionstore(K destination, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKey(destination).addKeys(keys); return createCommand(SUNIONSTORE, new IntegerOutput(codec), args); } @@ -849,6 +927,8 @@ public Command type(K key) { } public Command watch(K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKeys(keys); return createCommand(WATCH, new StatusOutput(codec), args); } @@ -870,6 +950,9 @@ public Command zadd(K key, double score, V member) { @SuppressWarnings("unchecked") public Command zadd(K key, Object... scoresAndValues) { + assertNotEmpty(scoresAndValues, "scoresAndValues " + MUST_NOT_BE_EMPTY); + assertNoNullElements(scoresAndValues, "scoresAndValues " + MUST_NOT_CONTAIN_NULL_ELEMENTS); + CommandArgs args = new CommandArgs(codec).addKey(key); for (int i = 0; i < scoresAndValues.length; i += 2) { args.add((Double) scoresAndValues[i]); @@ -897,10 +980,14 @@ public Command zincrby(K key, double amount, K member) { } public Command zinterstore(K destination, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + return zinterstore(destination, new ZStoreArgs(), keys); } public Command zinterstore(K destination, ZStoreArgs storeArgs, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).addKey(destination).add(keys.length).addKeys(keys); storeArgs.build(args); return createCommand(ZINTERSTORE, new IntegerOutput(codec), args); @@ -1015,6 +1102,8 @@ public Command zrank(K key, V member) { } public Command zrem(K key, V... members) { + assertNotEmpty(members, "members " + MUST_NOT_BE_EMPTY); + return createCommand(ZREM, new IntegerOutput(codec), key, members); } @@ -1145,10 +1234,14 @@ public Command zscore(K key, V member) { } public Command zunionstore(K destination, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + return zunionstore(destination, new ZStoreArgs(), keys); } public Command zunionstore(K destination, ZStoreArgs storeArgs, K... keys) { + assertNotEmpty(keys, "keys " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec); args.addKey(destination).add(keys.length).addKeys(keys); storeArgs.build(args); @@ -1374,17 +1467,30 @@ public Command zscanStreaming(ScoredValueStreamingChanne } public Command pfadd(K key, V value, V... moreValues) { + assertNotNull(value, "value " + MUST_NOT_BE_NULL); + assertNotNull(moreValues, "moreValues " + MUST_NOT_BE_NULL); + assertNoNullElements(moreValues, "moreValues " + MUST_NOT_CONTAIN_NULL_ELEMENTS); + CommandArgs args = new CommandArgs(codec).addKey(key).addValue(value).addValues(moreValues); return createCommand(PFADD, new IntegerOutput(codec), args); } public Command pfcount(K key, K... moreKeys) { + assertNotNull(key, "key " + MUST_NOT_BE_NULL); + assertNotNull(moreKeys, "moreKeys " + MUST_NOT_BE_NULL); + assertNoNullElements(moreKeys, "moreKeys " + MUST_NOT_CONTAIN_NULL_ELEMENTS); + CommandArgs args = new CommandArgs(codec).addKey(key).addKeys(moreKeys); return createCommand(PFCOUNT, new IntegerOutput(codec), args); } @SuppressWarnings("unchecked") public Command pfmerge(K destkey, K sourcekey, K... moreSourceKeys) { + assertNotNull(destkey, "destkey " + MUST_NOT_BE_NULL); + assertNotNull(sourcekey, "sourcekey " + MUST_NOT_BE_NULL); + assertNotNull(moreSourceKeys, "moreSourceKeys " + MUST_NOT_BE_NULL); + assertNoNullElements(moreSourceKeys, "moreSourceKeys " + MUST_NOT_CONTAIN_NULL_ELEMENTS); + CommandArgs args = new CommandArgs(codec).addKeys(destkey).addKey(sourcekey).addKeys(moreSourceKeys); return createCommand(PFADD, new IntegerOutput(codec), args); } @@ -1400,6 +1506,8 @@ public Command clusterForget(String nodeId) { } public Command clusterAddslots(int[] slots) { + assertNotEmpty(slots, "slots " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).add(ADDSLOTS); for (int slot : slots) { @@ -1409,6 +1517,8 @@ public Command clusterAddslots(int[] slots) { } public Command clusterDelslots(int[] slots) { + assertNotEmpty(slots, "slots " + MUST_NOT_BE_EMPTY); + CommandArgs args = new CommandArgs(codec).add(DELSLOTS); for (int slot : slots) { @@ -1507,4 +1617,73 @@ public Command clusterReset(boolean hard) { return createCommand(CLUSTER, new StatusOutput(codec), args); } + /** + * 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 message the exception message to use if the assertion fails + * @throws IllegalArgumentException if the object is {@code null} or the underlying string is empty + */ + protected static void assertNotEmpty(String string, String message) { + if (string == null || string.isEmpty()) { + throw new IllegalArgumentException(message); + } + } + + /** + * Assert that an object is not {@code null} . + * + * @param object the object to check + * @param message the exception message to use if the assertion fails + * @throws IllegalArgumentException if the object is {@code null} + */ + public static void assertNotNull(Object object, String message) { + if (object == null) { + throw new IllegalArgumentException(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 message the exception message to use if the assertion fails + * @throws IllegalArgumentException if the object array is {@code null} or has no elements + */ + protected void assertNotEmpty(Object[] array, String message) { + if (array == null || array.length == 0) { + throw new IllegalArgumentException(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 message the exception message to use if the assertion fails + * @throws IllegalArgumentException if the object array is {@code null} or has no elements + */ + protected static void assertNotEmpty(int[] array, String message) { + if (array == null || array.length == 0) { + throw new IllegalArgumentException(message); + } + } + + /** + * Assert that an array has no null elements. + * + * @param array the array to check + * @param message the exception message to use if the assertion fails + * @throws IllegalArgumentException if the object array contains a {@code null} element + */ + public static void assertNoNullElements(Object[] array, String message) { + if (array != null) { + for (Object element : array) { + if (element == null) { + throw new IllegalArgumentException(message); + } + } + } + } + } diff --git a/src/test/java/com/lambdaworks/redis/HLLCommandTest.java b/src/test/java/com/lambdaworks/redis/HLLCommandTest.java index bf5610990f..f1c527b3c1 100644 --- a/src/test/java/com/lambdaworks/redis/HLLCommandTest.java +++ b/src/test/java/com/lambdaworks/redis/HLLCommandTest.java @@ -3,6 +3,7 @@ package com.lambdaworks.redis; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import org.junit.Rule; import org.junit.Test; @@ -17,6 +18,22 @@ public void pfadd() throws Exception { assertThat(redis.pfadd(key, value, value)).isEqualTo(1); assertThat(redis.pfadd(key, value, value)).isEqualTo(0); + + assertThat(redis.pfadd(key, value)).isEqualTo(0); + } + + @Test + public void pfaddNullValues() throws Exception { + try { + redis.pfadd(key, null); + fail("Missing IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + try { + redis.pfadd(key, value, null); + fail("Missing IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } } @Test diff --git a/src/test/java/com/lambdaworks/redis/ServerCommandTest.java b/src/test/java/com/lambdaworks/redis/ServerCommandTest.java index c69044901b..2334cfaa7d 100644 --- a/src/test/java/com/lambdaworks/redis/ServerCommandTest.java +++ b/src/test/java/com/lambdaworks/redis/ServerCommandTest.java @@ -66,6 +66,11 @@ public void clientKill() throws Exception { assertThat(redis.clientKill(m.group(1))).isEqualTo("OK"); } + @Test(expected = IllegalArgumentException.class) + public void clientKillEmpty() throws Exception { + redis.clientKill(""); + } + @Test public void clientKillExtended() throws Exception { @@ -173,8 +178,8 @@ public void debugObject() throws Exception { */ @Test public void debugSegfault() throws Exception { - final RedisAsyncConnection connection = client.connectAsync( - RedisURI.Builder.redis(host(), port(3)).build()); + final RedisAsyncConnection connection = client.connectAsync(RedisURI.Builder.redis(host(), port(3)) + .build()); connection.debugSegfault(); WaitFor.waitOrTimeout(new Condition() { @Override @@ -226,11 +231,15 @@ public void save() throws Exception { @Test public void slaveof() throws Exception { - assertThat(redis.slaveof(TestSettings.host(), 0)).isEqualTo("OK"); redis.slaveofNoOne(); } + @Test(expected = IllegalArgumentException.class) + public void slaveofEmptyHost() throws Exception { + redis.slaveof("", 0); + } + @Test public void role() throws Exception {