diff --git a/src/main/java/net/spy/memcached/ArcusClient.java b/src/main/java/net/spy/memcached/ArcusClient.java index 3f09153f8..b5814a575 100644 --- a/src/main/java/net/spy/memcached/ArcusClient.java +++ b/src/main/java/net/spy/memcached/ArcusClient.java @@ -200,7 +200,6 @@ public class ArcusClient extends FrontCacheMemcachedClient implements ArcusClien private static final int MAX_GETBULK_ELEMENT_COUNT = 50; private static final int MAX_SMGET_COUNT = 1000; // server configuration is 2000. - private static final int MAX_MKEY_LENGTH = 250; private static final int SHUTDOWN_TIMEOUT_MILLISECONDS = 2000; private static final AtomicInteger CLIENT_ID = new AtomicInteger(1); @@ -447,28 +446,14 @@ public void shutdown() { this.shutdown(SHUTDOWN_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); } - private void validateMKey(String mkey) { - byte[] keyBytes = KeyUtil.getKeyBytes(mkey); - if (keyBytes.length > MAX_MKEY_LENGTH) { - throw new IllegalArgumentException("MKey is too long (maxlen = " - + MAX_MKEY_LENGTH + ")"); - } - if (keyBytes.length == 0) { - throw new IllegalArgumentException("MKey must contain at least one character."); - } - // Validate the mkey - for (byte b : keyBytes) { - if (b == ' ' || b == '\n' || b == '\r' || b == 0) { - throw new IllegalArgumentException("MKey contains invalid characters: ``" - + mkey + "''"); - } - } - } - public Transcoder getCollectionTranscoder() { return collectionTranscoder; } + public KeyValidator getKeyValidator() { + return keyValidator; + } + public AsyncArcusCommands asyncCommands() { return new AsyncArcusCommands<>(() -> this); } @@ -849,7 +834,7 @@ public Future> asyncStoreBulk(final StoreType t final List keyList, final int exp, final T o, final Transcoder tc) { - validateKeys(keyList); + keyValidator.validateKey(keyList); final CachedData co = tc.encode(o); final CountDownLatch latch = new CountDownLatch(keyList.size()); @@ -894,9 +879,7 @@ public Future> asyncStoreBulk(final StoreType t throw new IllegalArgumentException("Map is empty."); } - for (final String key : o.keySet()) { - validateKey(key); - } + keyValidator.validateKey(o.keySet()); final CountDownLatch latch = new CountDownLatch(o.size()); final BulkOperationFuture rv = new BulkOperationFuture<>(latch, operationTimeout); @@ -932,7 +915,7 @@ public Future> asyncStoreBulk(StoreType type, @Override public Future> asyncDeleteBulk(List keyList) { - validateKeys(keyList); + keyValidator.validateKey(keyList); final CountDownLatch latch = new CountDownLatch(keyList.size()); final BulkOperationFuture rv = new BulkOperationFuture<>(latch, operationTimeout); @@ -1105,7 +1088,7 @@ public CollectionFuture>> asyncBopGet(String key, boolean withDelete, boolean dropIfEmpty, Transcoder tc) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeGet get = new BTreeGet(bkey, eFlagFilter, withDelete, dropIfEmpty); return asyncBopGet(key, get, tc); } @@ -1127,7 +1110,7 @@ public CollectionFuture>> asyncBopGet(String key, boolean withDelete, boolean dropIfEmpty, Transcoder tc) { - BTreeUtil.validateBkey(from, to); + KeyValidator.validateBKey(from, to); BTreeGet get = new BTreeGet(from, to, eFlagFilter, offset, count, withDelete, dropIfEmpty); return asyncBopGet(key, get, tc); } @@ -1194,10 +1177,7 @@ public CollectionFuture> asyncMopGet(String key, String mkey, boolean withDelete, boolean dropIfEmpty, Transcoder tc) { - if (mkey == null) { - throw new IllegalArgumentException("mkey is null"); - } - validateMKey(mkey); + keyValidator.validateMKey(mkey); List mkeyList = new ArrayList<>(1); mkeyList.add(mkey); MapGet get = new MapGet(mkeyList, withDelete, dropIfEmpty); @@ -1214,12 +1194,8 @@ public CollectionFuture> asyncMopGet(String key, List mkeyList, boolean withDelete, boolean dropIfEmpty, Transcoder tc) { - if (mkeyList == null) { - throw new IllegalArgumentException("mkeyList is null"); - } - for (int i = 0; i < mkeyList.size(); i++) { - validateMKey(mkeyList.get(i)); - } + keyValidator.validateMKey(mkeyList); + MapGet get = new MapGet(mkeyList, withDelete, dropIfEmpty); return asyncMopGet(key, get, tc); } @@ -1301,7 +1277,7 @@ public CollectionFuture> asyncSopGet(String key, int count, public CollectionFuture asyncBopDelete(String key, long bkey, ElementFlagFilter eFlagFilter, boolean dropIfEmpty) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeDelete delete = new BTreeDelete(bkey, eFlagFilter, dropIfEmpty, false); return asyncCollectionDelete(key, delete); } @@ -1311,7 +1287,7 @@ public CollectionFuture asyncBopDelete(String key, long from, long to, ElementFlagFilter eFlagFilter, int count, boolean dropIfEmpty) { - BTreeUtil.validateBkey(from, to); + KeyValidator.validateBKey(from, to); BTreeDelete delete = new BTreeDelete(from, to, count, eFlagFilter, dropIfEmpty, false); return asyncCollectionDelete(key, delete); } @@ -1327,10 +1303,7 @@ public CollectionFuture asyncMopDelete(String key, @Override public CollectionFuture asyncMopDelete(String key, String mkey, boolean dropIfEmpty) { - if (mkey == null) { - throw new IllegalArgumentException("mkey is null"); - } - validateMKey(mkey); + keyValidator.validateMKey(mkey); List mkeyList = new ArrayList<>(1); mkeyList.add(mkey); MapDelete delete = new MapDelete(mkeyList, dropIfEmpty, false); @@ -1345,7 +1318,7 @@ public CollectionFuture asyncMopDelete(String key, String mkey, // throw new IllegalArgumentException("mkeyList is null"); // } // for (int i = 0; i < mkeyList.size(); i++) { -// validateMKey(mkeyList.get(i)); +// keyValidator.validateMKey(mkeyList.get(i), MAX_MKEY_LENGTH); // } // MapDelete delete = new MapDelete(mkeyList, false, dropIfEmpty); // return asyncCollectionDelete(key, delete); @@ -1423,7 +1396,7 @@ public void complete() { public CollectionFuture asyncBopGetItemCount(String key, long from, long to, ElementFlagFilter eFlagFilter) { - BTreeUtil.validateBkey(from, to); + KeyValidator.validateBKey(from, to); CollectionCount collectionCount = new BTreeCount(from, to, eFlagFilter); return asyncCollectionCount(key, collectionCount); } @@ -1460,7 +1433,7 @@ public CollectionFuture asyncBopInsert(String key, long bkey, byte[] eFlag, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeInsert bTreeInsert = new BTreeInsert<>(value, eFlag, null, attributesForCreate); return asyncCollectionInsert(key, String.valueOf(bkey), bTreeInsert, tc); } @@ -1470,7 +1443,7 @@ public CollectionFuture asyncMopInsert(String key, String mkey, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - validateMKey(mkey); + keyValidator.validateMKey(mkey); MapInsert mapInsert = new MapInsert<>(value, null, attributesForCreate); return asyncCollectionInsert(key, mkey, mapInsert, tc); } @@ -1583,9 +1556,7 @@ public CollectionFuture> asyncMopPip "The number of piped operations must be larger than 0."); } - for (Map.Entry checkMKey : elements.entrySet()) { - validateMKey(checkMKey.getKey()); - } + keyValidator.validateMKey(elements.keySet()); List> insertList = new ArrayList<>(); @@ -1841,7 +1812,7 @@ public CollectionFuture asyncBopUpsert(String key, long bkey, CollectionAttributes attributesForCreate, Transcoder tc) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeUpsert bTreeUpsert = new BTreeUpsert<>(value, elementFlag, null, attributesForCreate); return asyncCollectionInsert(key, String.valueOf(bkey), bTreeUpsert, tc); @@ -1857,7 +1828,7 @@ public CollectionFuture asyncMopUpsert(String key, String mkey, Object public CollectionFuture asyncMopUpsert(String key, String mkey, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - validateMKey(mkey); + keyValidator.validateMKey(mkey); MapUpsert mapUpsert = new MapUpsert<>(value, attributesForCreate); return asyncCollectionInsert(key, mkey, mapUpsert, tc); @@ -1873,7 +1844,7 @@ public CollectionFuture asyncBopUpdate(String key, long bkey, public CollectionFuture asyncBopUpdate(String key, long bkey, ElementFlagUpdate eFlagUpdate, T value, Transcoder tc) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeUpdate collectionUpdate = new BTreeUpdate<>(value, eFlagUpdate, false); return asyncCollectionUpdate(key, String.valueOf(bkey), collectionUpdate, tc); } @@ -1890,7 +1861,7 @@ public CollectionFuture asyncBopUpdate(String key, byte[] bkey, ElementFlagUpdate eFlagUpdate, T value, Transcoder tc) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeUpdate collectionUpdate = new BTreeUpdate<>(value, eFlagUpdate, false); return asyncCollectionUpdate(key, BTreeUtil.toHex(bkey), collectionUpdate, tc); } @@ -1904,7 +1875,7 @@ public CollectionFuture asyncMopUpdate(String key, String mkey, @Override public CollectionFuture asyncMopUpdate(String key, String mkey, T value, Transcoder tc) { - validateMKey(mkey); + keyValidator.validateMKey(mkey); MapUpdate collectionUpdate = new MapUpdate<>(value, false); return asyncCollectionUpdate(key, mkey, collectionUpdate, tc); } @@ -2001,9 +1972,7 @@ public CollectionFuture> asyncMopPip "The number of piped operations must be larger than 0."); } - for (Map.Entry checkMKey : elements.entrySet()) { - validateMKey(checkMKey.getKey()); - } + keyValidator.validateMKey(elements.keySet()); List> updateList = new ArrayList<>(); @@ -2084,7 +2053,7 @@ public CollectionFuture asyncBopInsert(String key, byte[] bkey, byte[] eFlag, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeInsert bTreeInsert = new BTreeInsert<>(value, eFlag, null, attributesForCreate); return asyncCollectionInsert(key, BTreeUtil.toHex(bkey), bTreeInsert, tc); } @@ -2112,7 +2081,7 @@ public CollectionFuture>> asyncBopGet( public CollectionFuture>> asyncBopGet( String key, byte[] bkey, ElementFlagFilter eFlagFilter, boolean withDelete, boolean dropIfEmpty, Transcoder tc) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeGet get = new BTreeGet(bkey, eFlagFilter, withDelete, dropIfEmpty); return asyncBopExtendedGet(key, get, tc); } @@ -2144,7 +2113,7 @@ public CollectionFuture>> asyncBopGet( String key, byte[] from, byte[] to, ElementFlagFilter eFlagFilter, int offset, int count, boolean withDelete, boolean dropIfEmpty, Transcoder tc) { - BTreeUtil.validateBkey(from, to); + KeyValidator.validateBKey(from, to); BTreeGet get = new BTreeGet(from, to, eFlagFilter, offset, count, withDelete, dropIfEmpty); return asyncBopExtendedGet(key, get, tc); } @@ -2274,7 +2243,7 @@ public void gotData(int pos, int flags, BKeyObject bkeyObject, byte[] eflag, byt @Override public CollectionFuture asyncBopFindPosition(String key, long bkey, BTreeOrder order) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); if (order == null) { throw new IllegalArgumentException("BTreeOrder must not be null."); } @@ -2285,7 +2254,7 @@ public CollectionFuture asyncBopFindPosition(String key, long bkey, @Override public CollectionFuture asyncBopFindPosition(String key, byte[] bkey, BTreeOrder order) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); if (order == null) { throw new IllegalArgumentException("BTreeOrder must not be null."); } @@ -2526,7 +2495,7 @@ public CollectionFuture asyncBopDelete(String key, byte[] from, byte[] to, ElementFlagFilter eFlagFilter, int count, boolean dropIfEmpty) { - BTreeUtil.validateBkey(from, to); + KeyValidator.validateBKey(from, to); BTreeDelete delete = new BTreeDelete(from, to, count, eFlagFilter, dropIfEmpty, false); return asyncCollectionDelete(key, delete); } @@ -2535,7 +2504,7 @@ public CollectionFuture asyncBopDelete(String key, public CollectionFuture asyncBopDelete(String key, byte[] bkey, ElementFlagFilter eFlagFilter, boolean dropIfEmpty) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeDelete delete = new BTreeDelete(bkey, eFlagFilter, dropIfEmpty, false); return asyncCollectionDelete(key, delete); } @@ -2552,7 +2521,7 @@ public CollectionFuture asyncBopUpsert(String key, byte[] bkey, byte[] elementFlag, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); BTreeUpsert bTreeUpsert = new BTreeUpsert<>(value, elementFlag, null, attributesForCreate); return asyncCollectionInsert(key, BTreeUtil.toHex(bkey), bTreeUpsert, tc); } @@ -2561,7 +2530,7 @@ public CollectionFuture asyncBopUpsert(String key, public CollectionFuture asyncBopGetItemCount(String key, byte[] from, byte[] to, ElementFlagFilter eFlagFilter) { - BTreeUtil.validateBkey(from, to); + KeyValidator.validateBKey(from, to); CollectionCount collectionCount = new BTreeCount(from, to, eFlagFilter); return asyncCollectionCount(key, collectionCount); } @@ -2655,8 +2624,8 @@ public SMGetFuture>> asyncBopSortMergeGet( public SMGetFuture>> asyncBopSortMergeGet( List keyList, byte[] from, byte[] to, ElementFlagFilter eFlagFilter, int count, boolean unique) { - BTreeUtil.validateBkey(from, to); - validateKeys(keyList); + KeyValidator.validateBKey(from, to); + keyValidator.validateKey(keyList); checkDupKey(keyList); if (count < 1) { throw new IllegalArgumentException("Count must be larger than 0."); @@ -2683,7 +2652,7 @@ public SMGetFuture>> asyncBopSortMergeGet( public SMGetFuture>> asyncBopSortMergeGet( List keyList, long from, long to, ElementFlagFilter eFlagFilter, int count, boolean unique) { - validateKeys(keyList); + keyValidator.validateKey(keyList); checkDupKey(keyList); if (count < 1) { throw new IllegalArgumentException("Count must be larger than 0."); @@ -2717,8 +2686,8 @@ public Future> asyncBopInsertBulk( List keyList, long bkey, byte[] eFlag, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - BTreeUtil.validateBkey(bkey); - validateKeys(keyList); + KeyValidator.validateBKey(bkey); + keyValidator.validateKey(keyList); checkDupKey(keyList); Collection>> arrangedKey = groupingKeys(keyList, NON_PIPED_BULK_INSERT_CHUNK_SIZE, APIType.BOP_INSERT); @@ -2751,8 +2720,8 @@ public Future> asyncBopInsertBulk( List keyList, byte[] bkey, byte[] eFlag, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - BTreeUtil.validateBkey(bkey); - validateKeys(keyList); + KeyValidator.validateBKey(bkey); + keyValidator.validateKey(keyList); checkDupKey(keyList); Collection>> arrangedKey = groupingKeys(keyList, NON_PIPED_BULK_INSERT_CHUNK_SIZE, APIType.BOP_INSERT); @@ -2783,8 +2752,8 @@ public Future> asyncMopInsertBulk( List keyList, String mkey, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - validateMKey(mkey); - validateKeys(keyList); + keyValidator.validateMKey(mkey); + keyValidator.validateKey(keyList); checkDupKey(keyList); Collection>> arrangedKey = groupingKeys(keyList, NON_PIPED_BULK_INSERT_CHUNK_SIZE, APIType.MOP_INSERT); @@ -2814,7 +2783,7 @@ public Future> asyncSopInsertBulk( public Future> asyncSopInsertBulk( List keyList, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - validateKeys(keyList); + keyValidator.validateKey(keyList); checkDupKey(keyList); Collection>> arrangedKey = @@ -2844,7 +2813,7 @@ public Future> asyncLopInsertBulk( public Future> asyncLopInsertBulk( List keyList, int index, T value, CollectionAttributes attributesForCreate, Transcoder tc) { - validateKeys(keyList); + keyValidator.validateKey(keyList); checkDupKey(keyList); Collection>> arrangedKey = @@ -2912,7 +2881,8 @@ public CollectionGetBulkFuture>> asyncB public CollectionGetBulkFuture>> asyncBopGetBulk( List keyList, long from, long to, ElementFlagFilter eFlagFilter, int offset, int count, Transcoder tc) { - validateKeys(keyList); + KeyValidator.validateBKey(from, to); + keyValidator.validateKey(keyList); checkDupKey(keyList); if (offset < 0) { throw new IllegalArgumentException("Offset must be 0 or positive integer."); @@ -2943,7 +2913,6 @@ public CollectionGetBulkFuture>> asyncBo asyncBopGetBulk( List keyList, byte[] from, byte[] to, ElementFlagFilter eFlagFilter, int offset, int count) { - BTreeUtil.validateBkey(from, to); return asyncBopGetBulk(keyList, from, to, eFlagFilter, offset, count, collectionTranscoder); } @@ -2951,8 +2920,8 @@ public CollectionGetBulkFuture> List keyList, byte[] from, byte[] to, ElementFlagFilter eFlagFilter, int offset, int count, Transcoder tc) { - BTreeUtil.validateBkey(from, to); - validateKeys(keyList); + KeyValidator.validateBKey(from, to); + keyValidator.validateKey(keyList); checkDupKey(keyList); if (offset < 0) { @@ -3102,7 +3071,7 @@ public void gotElement(String key, int flags, Object bkey, byte[] eflag, byte[] @Override public CollectionFuture asyncBopIncr(String key, long bkey, int by) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); CollectionMutate collectionMutate = new BTreeMutate(Mutator.incr, by); return asyncCollectionMutate(key, String.valueOf(bkey), collectionMutate); } @@ -3110,7 +3079,7 @@ public CollectionFuture asyncBopIncr(String key, long bkey, @Override public CollectionFuture asyncBopIncr(String key, byte[] bkey, int by) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); CollectionMutate collectionMutate = new BTreeMutate(Mutator.incr, by); return asyncCollectionMutate(key, BTreeUtil.toHex(bkey), collectionMutate); } @@ -3118,7 +3087,7 @@ public CollectionFuture asyncBopIncr(String key, byte[] bkey, @Override public CollectionFuture asyncBopIncr(String key, long bkey, int by, long initial, byte[] eFlag) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); CollectionMutate collectionMutate = new BTreeMutate(Mutator.incr, by, initial, eFlag); return asyncCollectionMutate(key, String.valueOf(bkey), collectionMutate); } @@ -3126,7 +3095,7 @@ public CollectionFuture asyncBopIncr(String key, long bkey, @Override public CollectionFuture asyncBopIncr(String key, byte[] bkey, int by, long initial, byte[] eFlag) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); CollectionMutate collectionMutate = new BTreeMutate(Mutator.incr, by, initial, eFlag); return asyncCollectionMutate(key, BTreeUtil.toHex(bkey), collectionMutate); } @@ -3134,7 +3103,7 @@ public CollectionFuture asyncBopIncr(String key, byte[] bkey, @Override public CollectionFuture asyncBopDecr(String key, long bkey, int by) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); CollectionMutate collectionMutate = new BTreeMutate(Mutator.decr, by); return asyncCollectionMutate(key, String.valueOf(bkey), collectionMutate); } @@ -3142,7 +3111,7 @@ public CollectionFuture asyncBopDecr(String key, long bkey, @Override public CollectionFuture asyncBopDecr(String key, byte[] bkey, int by) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); CollectionMutate collectionMutate = new BTreeMutate(Mutator.decr, by); return asyncCollectionMutate(key, BTreeUtil.toHex(bkey), collectionMutate); } @@ -3150,7 +3119,7 @@ public CollectionFuture asyncBopDecr(String key, byte[] bkey, @Override public CollectionFuture asyncBopDecr(String key, long bkey, int by, long initial, byte[] eFlag) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); CollectionMutate collectionMutate = new BTreeMutate(Mutator.decr, by, initial, eFlag); return asyncCollectionMutate(key, String.valueOf(bkey), collectionMutate); } @@ -3158,7 +3127,7 @@ public CollectionFuture asyncBopDecr(String key, long bkey, @Override public CollectionFuture asyncBopDecr(String key, byte[] bkey, int by, long initial, byte[] eFlag) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); CollectionMutate collectionMutate = new BTreeMutate(Mutator.decr, by, initial, eFlag); return asyncCollectionMutate(key, BTreeUtil.toHex(bkey), collectionMutate); } @@ -3211,18 +3180,6 @@ public void complete() { return rv; } - private void validateKeys(Collection keyList) { - if (keyList == null) { - throw new IllegalArgumentException("Key list is null."); - } else if (keyList.isEmpty()) { - throw new IllegalArgumentException("Key list is empty."); - } - - for (String key : keyList) { - validateKey(key); - } - } - private void checkDupKey(Collection keyList) { /* * Dup Check -> insure elements sequentially added to keyList diff --git a/src/main/java/net/spy/memcached/KeyValidator.java b/src/main/java/net/spy/memcached/KeyValidator.java new file mode 100644 index 000000000..21289a735 --- /dev/null +++ b/src/main/java/net/spy/memcached/KeyValidator.java @@ -0,0 +1,180 @@ +/* + * arcus-java-client : Arcus Java client + * Copyright 2010-2014 NAVER Corp. + * Copyright 2014-present JaM2in Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.spy.memcached; + +import java.util.Collection; + +/** + * Validator for memcached keys. + */ +public final class KeyValidator { + + private static final int MAX_KEY_LENGTH = 4000; + private static final int MAX_MKEY_LENGTH = 250; + private static final int MAX_BKEY_BYTE_ARRAY_LENGTH = 31; + + private final byte delimiter; + + public KeyValidator(byte delimiter) { + this.delimiter = delimiter; + } + + /** + * Validate cache key. + * + * @param key the cache key to validate + * @throws IllegalArgumentException if the key is invalid + */ + public void validateKey(String key) { + boolean hasPrefix = false; + + byte[] keyBytes = KeyUtil.getKeyBytes(key); + if (keyBytes.length > MAX_KEY_LENGTH) { + throw new IllegalArgumentException("Key is too long (maxlen = " + + MAX_KEY_LENGTH + ")"); + } else if (keyBytes.length == 0) { + throw new IllegalArgumentException( + "Key must contain at least one character."); + } + // Validate the key + for (byte b : keyBytes) { + if (b == ' ' || b == '\n' || b == '\r' || b == 0) { + throw new IllegalArgumentException( + "Key contains invalid characters: ``" + key + "''"); + } + if (b == delimiter) { + hasPrefix = true; + } + } + + // Validate the prefix + if (hasPrefix) { + if (keyBytes[0] == '-') { + throw new IllegalArgumentException( + "Key contains invalid prefix: ``" + key + "''"); + } + for (byte b : keyBytes) { + if (b == delimiter) { + break; + } + if (!(('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z') || + ('0' <= b && b <= '9') || + (b == '_') || (b == '-') || (b == '+') || (b == '.'))) { + throw new IllegalArgumentException( + "Key contains invalid prefix: ``" + key + "''"); + } + } + } + } + + /** + * Validate cache keys. + * + * @param keyList the cache keys to validate + * @throws IllegalArgumentException if the key list is null, empty, or contains invalid keys + */ + public void validateKey(Collection keyList) { + if (keyList == null) { + throw new IllegalArgumentException("Key list is null."); + } else if (keyList.isEmpty()) { + throw new IllegalArgumentException("Key list is empty."); + } + + for (String key : keyList) { + validateKey(key); + } + } + + /** + * Validate map key. + * + * @param mkey the mkey to validate + * @throws IllegalArgumentException if the mkey is invalid + */ + public void validateMKey(String mkey) { + if (mkey == null) { + throw new IllegalArgumentException("mkey is null"); + } + + byte[] keyBytes = KeyUtil.getKeyBytes(mkey); + if (keyBytes.length > MAX_MKEY_LENGTH) { + throw new IllegalArgumentException("MKey is too long (maxlen = " + + MAX_MKEY_LENGTH + ")"); + } + if (keyBytes.length == 0) { + throw new IllegalArgumentException("MKey must contain at least one character."); + } + // Validate the mkey + for (byte b : keyBytes) { + if (b == ' ' || b == '\n' || b == '\r' || b == 0) { + throw new IllegalArgumentException("MKey contains invalid characters: ``" + + mkey + "''"); + } + } + } + + /** + * Validate map keys. + * + * @param mkeyList the mkeys to validate + * @throws IllegalArgumentException if the mkey list is null, empty, or contains invalid mkeys + */ + public void validateMKey(Collection mkeyList) { + if (mkeyList == null) { + throw new IllegalArgumentException("mkeyList is null."); + } else if (mkeyList.isEmpty()) { + throw new IllegalArgumentException("mkeyList is empty."); + } + + for (String mkey : mkeyList) { + validateMKey(mkey); + } + } + + /** + * Validate byte type bkeys. + * + * @param bkeys the bkeys to validate + * @throws IllegalArgumentException if the bkey is invalid + */ + public static void validateBKey(byte[]... bkeys) { + for (byte[] bkey : bkeys) { + if (bkey == null) { + throw new IllegalArgumentException("bkey is null"); + } + if (bkey.length > MAX_BKEY_BYTE_ARRAY_LENGTH) { + throw new IllegalArgumentException("bkey size exceeded 31"); + } + } + } + + /** + * Validate long type bkeys. + * + * @param bkeys the bkeys to validate + * @throws IllegalArgumentException if the bkey is invalid + */ + public static void validateBKey(long... bkeys) { + for (long bkey : bkeys) { + if (bkey < 0) { + throw new IllegalArgumentException( + String.format("not supported unsigned long bkey : %s, use byte array bkey", bkey)); + } + } + } +} diff --git a/src/main/java/net/spy/memcached/MemcachedClient.java b/src/main/java/net/spy/memcached/MemcachedClient.java index 0b0284a63..ff3e8dfcf 100644 --- a/src/main/java/net/spy/memcached/MemcachedClient.java +++ b/src/main/java/net/spy/memcached/MemcachedClient.java @@ -130,7 +130,7 @@ public class MemcachedClient extends SpyThread protected final Transcoder transcoder; - private final byte delimiter; + protected final KeyValidator keyValidator; private static final String DEFAULT_MEMCACHED_CLIENT_NAME = "MemcachedClient"; @@ -213,7 +213,7 @@ public MemcachedClient(ConnectionFactory cf, String name, List MAX_KEY_LENGTH) { - throw new IllegalArgumentException("Key is too long (maxlen = " - + MAX_KEY_LENGTH + ")"); - } - if (keyBytes.length == 0) { - throw new IllegalArgumentException( - "Key must contain at least one character."); - } - // Validate the key - for (byte b : keyBytes) { - if (b == ' ' || b == '\n' || b == '\r' || b == 0) { - throw new IllegalArgumentException( - "Key contains invalid characters: ``" + key + "''"); - } - if (b == delimiter) { - hasPrefix = true; - } - } - - // Validate the prefix - if (hasPrefix) { - if (keyBytes[0] == '-') { - throw new IllegalArgumentException( - "Key contains invalid prefix: ``" + key + "''"); - } - for (byte b : keyBytes) { - if (b == delimiter) { - break; - } - if (!(('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z') || - ('0' <= b && b <= '9') || - (b == '_') || (b == '-') || (b == '+') || (b == '.'))) { - throw new IllegalArgumentException( - "Key contains invalid prefix: ``" + key + "''"); - } - } - } - } - protected void checkState() { if (shuttingDown) { throw new IllegalStateException("Shutting down"); @@ -342,7 +299,7 @@ protected void checkState() { * @return the Operation */ public Operation addOp(final String key, final Operation op) { - validateKey(key); + keyValidator.validateKey(key); checkState(); conn.addOperation(key, op); return op; @@ -1067,7 +1024,7 @@ public BulkFuture> asyncGetBulk(Collection keys, for (String key : keys) { if (tcIter.hasNext()) { tcMap.put(key, tcIter.next()); - validateKey(key); + keyValidator.validateKey(key); } } @@ -1200,7 +1157,7 @@ public BulkFuture>> asyncGetsBulk(Collection for (String key : keys) { if (tcIter.hasNext()) { tcMap.put(key, tcIter.next()); - validateKey(key); + keyValidator.validateKey(key); } } diff --git a/src/main/java/net/spy/memcached/MemcachedClientIF.java b/src/main/java/net/spy/memcached/MemcachedClientIF.java index fc96753c6..5782cc597 100644 --- a/src/main/java/net/spy/memcached/MemcachedClientIF.java +++ b/src/main/java/net/spy/memcached/MemcachedClientIF.java @@ -15,10 +15,6 @@ * This interface is provided as a helper for testing clients of the MemcachedClient. */ public interface MemcachedClientIF { - /** - * Maximum supported key length. - */ - int MAX_KEY_LENGTH = 4000; Collection getAvailableServers(); diff --git a/src/main/java/net/spy/memcached/collection/BKeyObject.java b/src/main/java/net/spy/memcached/collection/BKeyObject.java index c9e597008..c1d4537a3 100644 --- a/src/main/java/net/spy/memcached/collection/BKeyObject.java +++ b/src/main/java/net/spy/memcached/collection/BKeyObject.java @@ -19,6 +19,7 @@ import java.util.Objects; +import net.spy.memcached.KeyValidator; import net.spy.memcached.util.BTreeUtil; public class BKeyObject implements Comparable { @@ -32,14 +33,14 @@ public enum BKeyType { private final ByteArrayBKey byteArrayBKey; public BKeyObject(long longBKey) { - BTreeUtil.validateBkey(longBKey); + KeyValidator.validateBKey(longBKey); this.type = BKeyType.LONG; this.longBKey = longBKey; this.byteArrayBKey = null; } public BKeyObject(byte[] byteArrayBKey) { - BTreeUtil.validateBkey(byteArrayBKey); + KeyValidator.validateBKey(byteArrayBKey); this.type = BKeyType.BYTEARRAY; this.longBKey = null; this.byteArrayBKey = new ByteArrayBKey(byteArrayBKey); diff --git a/src/main/java/net/spy/memcached/collection/ByteArrayBKey.java b/src/main/java/net/spy/memcached/collection/ByteArrayBKey.java index f1393788c..14c787973 100644 --- a/src/main/java/net/spy/memcached/collection/ByteArrayBKey.java +++ b/src/main/java/net/spy/memcached/collection/ByteArrayBKey.java @@ -19,6 +19,7 @@ import java.util.Arrays; +import net.spy.memcached.KeyValidator; import net.spy.memcached.util.BTreeUtil; public class ByteArrayBKey implements Comparable { @@ -37,7 +38,7 @@ public class ByteArrayBKey implements Comparable { private final byte[] bkey; public ByteArrayBKey(byte[] bkey) { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); this.bkey = bkey; } diff --git a/src/main/java/net/spy/memcached/util/BTreeUtil.java b/src/main/java/net/spy/memcached/util/BTreeUtil.java index 93312d455..7d7993e35 100644 --- a/src/main/java/net/spy/memcached/util/BTreeUtil.java +++ b/src/main/java/net/spy/memcached/util/BTreeUtil.java @@ -25,7 +25,6 @@ public final class BTreeUtil { private static final String HEXES = "0123456789ABCDEF"; - private static final int MAX_BKEY_BYTE_ARRAY_SIZE = 31; private BTreeUtil() { } @@ -86,26 +85,6 @@ public static int compareByteArraysInLexOrder(byte[] array1, byte[] array2) { return array1.length - array2.length; } - public static void validateBkey(byte[] ...bkeys) { - for (byte[] bkey : bkeys) { - if (bkey == null) { - throw new IllegalArgumentException("bkey is null"); - } - if (bkey.length > MAX_BKEY_BYTE_ARRAY_SIZE) { - throw new IllegalArgumentException("bkey size exceeded 31"); - } - } - } - - public static void validateBkey(long ...bkeys) { - for (long bkey : bkeys) { - if (bkey < 0) { - throw new IllegalArgumentException( - String.format("not supported unsigned long bkey : %s, use byte array bkey", bkey)); - } - } - } - public static Element makeBTreeElement(BKeyObject bkey, CachedData cachedData, Transcoder tc) { diff --git a/src/test/java/net/spy/memcached/util/BTreeUtilTest.java b/src/test/java/net/spy/memcached/util/BTreeUtilTest.java index 5b3564861..0c8c209e5 100644 --- a/src/test/java/net/spy/memcached/util/BTreeUtilTest.java +++ b/src/test/java/net/spy/memcached/util/BTreeUtilTest.java @@ -18,6 +18,8 @@ import java.util.Arrays; +import net.spy.memcached.KeyValidator; + import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -90,7 +92,7 @@ void testCompareDifferentLengthByteArrays() throws Exception { @Test void testInValidSizeBkey() { assertThrows(IllegalArgumentException.class, () -> { - BTreeUtil.validateBkey(new byte[32]); + KeyValidator.validateBKey(new byte[32]); }); } @@ -98,7 +100,7 @@ void testInValidSizeBkey() { void testMinusLongBkey() { final long bkey = -1; assertThrows(IllegalArgumentException.class, () -> { - BTreeUtil.validateBkey(bkey); + KeyValidator.validateBKey(bkey); }); } }