Skip to content

Commit 7d40455

Browse files
Mike WiederholdMichael Wiederhold
authored andcommitted
HashAlgorithm registry to add new algorithms from config
Change-Id: Ie323151b67608e454b726050bd80b097cfba724c Reviewed-on: http://review.couchbase.org/8560 Reviewed-by: Michael Wiederhold <mike@couchbase.com> Tested-by: Michael Wiederhold <mike@couchbase.com>
1 parent 6aa2332 commit 7d40455

6 files changed

Lines changed: 127 additions & 31 deletions

File tree

src/main/java/net/spy/memcached/DefaultHashAlgorithm.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ public enum DefaultHashAlgorithm implements HashAlgorithm {
3939
*/
4040
NATIVE_HASH,
4141
/**
42-
* CRC32_HASH as used by the perl API. This will be more consistent both
42+
* CRC_HASH as used by the perl API. This will be more consistent both
4343
* across multiple API users as well as java versions, but is mostly likely
4444
* significantly slower.
4545
*/
46-
CRC32_HASH,
46+
CRC_HASH,
4747
/**
4848
* FNV hashes are designed to be fast while maintaining a low collision rate.
4949
* The FNV speed allows one to quickly hash lots of data while maintaining a
@@ -100,7 +100,7 @@ public long hash(final String k) {
100100
case NATIVE_HASH:
101101
rv = k.hashCode();
102102
break;
103-
case CRC32_HASH:
103+
case CRC_HASH:
104104
// return (crc32(shift) >> 16) & 0x7fff;
105105
CRC32 crc32 = new CRC32();
106106
crc32.update(KeyUtil.getKeyBytes(k));
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* Copyright (C) 2009-2011 Couchbase, Inc.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
20+
* IN THE SOFTWARE.
21+
*/
22+
23+
package net.spy.memcached;
24+
25+
import java.util.HashMap;
26+
import java.util.Map;
27+
28+
/**
29+
* Registry of known hashing algorithms for locating a server for a key. Useful
30+
* when configuring from files using algorithm names.
31+
*
32+
* <p>
33+
* Please, make sure you register your algorithm with {
34+
* {@link #registerHashAlgorithm(String, HashAlgorithm)} before referring to it
35+
* by name
36+
*/
37+
public final class HashAlgorithmRegistry {
38+
39+
private HashAlgorithmRegistry() {
40+
// Empty
41+
}
42+
43+
/**
44+
* Internal registry storage.
45+
*/
46+
private static final Map<String, HashAlgorithm> REGISTRY =
47+
new HashMap<String, HashAlgorithm>();
48+
// initializing with the default algorithms implementation
49+
static {
50+
for (DefaultHashAlgorithm alg : DefaultHashAlgorithm.values()) {
51+
registerHashAlgorithm(alg.name().replace("_HASH", "").toLowerCase(),
52+
alg);
53+
}
54+
}
55+
56+
/**
57+
* Registers provided {@link HashAlgorithm} instance with the given name. Name
58+
* is not case sensitive. Any registered algorithm with the same name will be
59+
* substituted
60+
*
61+
* @param name name of the algorithm
62+
* @param alg algorithm instance to register
63+
*/
64+
public static synchronized void registerHashAlgorithm(String name,
65+
HashAlgorithm alg) {
66+
validateName(name);
67+
validateAlgorithm(alg);
68+
REGISTRY.put(name.toLowerCase(), alg);
69+
}
70+
71+
/**
72+
* Tries to find selected hash algorithm using name provided.
73+
*
74+
* <p>
75+
* Note, that lookup is being performed using name's lower-case value
76+
*
77+
* @param name the algorithm name to be used for lookup
78+
* @return a {@link HashAlgorithm} instance or <code>null</code> if there's no
79+
* algorithm with the specified name
80+
*/
81+
public static synchronized HashAlgorithm lookupHashAlgorithm(String name) {
82+
validateName(name);
83+
return REGISTRY.get(name.toLowerCase());
84+
}
85+
86+
/**
87+
* Validates name of the algorithm. A non-empty name should be provided An
88+
* {@link IllegalArgumentException} is being thrown in case of incorrect name
89+
*
90+
* @param name a name to validate
91+
*/
92+
private static void validateName(String name) {
93+
if (name == null || "".equals(name)) {
94+
throw new IllegalArgumentException("HashAlgorithm name should be"
95+
+ "provided in order to perform the lookup: either NULL or "
96+
+ "empty string has been provided");
97+
}
98+
}
99+
100+
/**
101+
* Validates algorithm instance. A non-<code>null</code> instance should be
102+
* provided. An {@link IllegalArgumentException} is being thrown in case of
103+
* <code>null</code> instance
104+
*
105+
* @param alg a {@link HashAlgorithm} instance to validate
106+
*/
107+
private static void validateAlgorithm(HashAlgorithm alg) {
108+
if (alg == null) {
109+
throw new IllegalArgumentException("HashAlgorithm instance should be "
110+
+ "provided in order to register a new algorithm");
111+
}
112+
}
113+
}

src/main/java/net/spy/memcached/TapConnectionProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,13 @@ public TapConnectionProvider(final List<URI> baseList,
152152
if (config.getConfigType() == ConfigType.MEMBASE) {
153153
cfb.setFailureMode(FailureMode.Retry)
154154
.setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
155-
.setHashAlg(DefaultHashAlgorithm.KETAMA_HASH)
155+
.setHashAlg(config.getHashAlgorithm())
156156
.setLocatorType(ConnectionFactoryBuilder.Locator.VBUCKET)
157157
.setVBucketConfig(bucket.getConfig());
158158
} else if (config.getConfigType() == ConfigType.MEMCACHE) {
159159
cfb.setFailureMode(FailureMode.Redistribute)
160160
.setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
161-
.setHashAlg(DefaultHashAlgorithm.KETAMA_HASH)
161+
.setHashAlg(config.getHashAlgorithm())
162162
.setLocatorType(ConnectionFactoryBuilder.Locator.CONSISTENT)
163163
.setShouldOptimize(false);
164164
} else {

src/main/java/net/spy/memcached/vbucket/config/DefaultConfigFactory.java

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
import java.util.ArrayList;
3131
import java.util.List;
3232

33-
import net.spy.memcached.DefaultHashAlgorithm;
3433
import net.spy.memcached.HashAlgorithm;
34+
import net.spy.memcached.HashAlgorithmRegistry;
3535

3636
import org.codehaus.jettison.json.JSONArray;
3737
import org.codehaus.jettison.json.JSONException;
@@ -83,27 +83,6 @@ public Config create(JSONObject jsonObject) {
8383
}
8484
}
8585

86-
private HashAlgorithm lookupHashAlgorithm(String algorithm) {
87-
HashAlgorithm ha = DefaultHashAlgorithm.NATIVE_HASH;
88-
if ("crc".equalsIgnoreCase(algorithm)) {
89-
ha = DefaultHashAlgorithm.CRC32_HASH;
90-
} else if ("fnv1_32".equalsIgnoreCase(algorithm)) {
91-
ha = DefaultHashAlgorithm.FNV1_32_HASH;
92-
} else if ("fnv1_64".equalsIgnoreCase(algorithm)) {
93-
ha = DefaultHashAlgorithm.FNV1_64_HASH;
94-
} else if ("fnv1a_32".equalsIgnoreCase(algorithm)) {
95-
ha = DefaultHashAlgorithm.FNV1A_32_HASH;
96-
} else if ("fnv1a_64".equalsIgnoreCase(algorithm)) {
97-
ha = DefaultHashAlgorithm.FNV1A_64_HASH;
98-
} else if ("md5".equalsIgnoreCase(algorithm)) {
99-
ha = DefaultHashAlgorithm.KETAMA_HASH;
100-
} else {
101-
throw new IllegalArgumentException("Unhandled algorithm type: "
102-
+ algorithm);
103-
}
104-
return ha;
105-
}
106-
10786
private Config parseJSON(JSONObject jsonObject) throws JSONException {
10887
// the incoming config could be cache or EP object types, JSON envelope
10988
// picked apart
@@ -129,9 +108,13 @@ private Config parseCacheJSON(JSONObject jsonObject) throws JSONException {
129108

130109
/* ep is for ep-engine, a.k.a. membase */
131110
private Config parseEpJSON(JSONObject jsonObject) throws JSONException {
132-
111+
String algorithm = jsonObject.getString("hashAlgorithm");
133112
HashAlgorithm hashAlgorithm =
134-
lookupHashAlgorithm(jsonObject.getString("hashAlgorithm"));
113+
HashAlgorithmRegistry.lookupHashAlgorithm(algorithm);
114+
if (hashAlgorithm == null) {
115+
throw new IllegalArgumentException("Unhandled hash algorithm type: "
116+
+ algorithm);
117+
}
135118
int replicasCount = jsonObject.getInt("numReplicas");
136119
if (replicasCount > VBucket.MAX_REPLICAS) {
137120
throw new ConfigParsingException("Expected number <= "

src/test/java/net/spy/memcached/DefaultHashAlgorithmTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void testCrc32Hash() {
5959
exp.put("UDATA:edevil@sapo.pt", 558L);
6060

6161
for (Map.Entry<String, Long> me : exp.entrySet()) {
62-
assertHash(DefaultHashAlgorithm.CRC32_HASH, me.getKey(), me.getValue());
62+
assertHash(DefaultHashAlgorithm.CRC_HASH, me.getKey(), me.getValue());
6363
}
6464
}
6565

src/test/java/net/spy/memcached/spring/MemcachedClientFactoryBeanTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public void testGetObject() throws Exception {
4747
final MemcachedClientFactoryBean factory = new MemcachedClientFactoryBean();
4848
factory.setDaemon(true);
4949
factory.setFailureMode(FailureMode.Cancel);
50-
factory.setHashAlg(DefaultHashAlgorithm.CRC32_HASH);
50+
factory.setHashAlg(DefaultHashAlgorithm.CRC_HASH);
5151
factory.setProtocol(Protocol.BINARY);
5252
factory.setServers(TestConfig.IPV4_ADDR + ":22211 " + TestConfig.IPV4_ADDR
5353
+ ":22212");

0 commit comments

Comments
 (0)