/*
* Copyright © 2014 Cask Data, Inc.
*
* 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 co.cask.cdap.hbase.wd;
import org.apache.hadoop.hbase.util.Bytes;
import java.util.Arrays;
/**
* Provides handy methods to distribute
*/
public class RowKeyDistributorByHashPrefix extends AbstractRowKeyDistributor {
private static final String DELIM = "--";
private Hasher hasher;
/** Constructor reflection. DO NOT USE */
public RowKeyDistributorByHashPrefix() {
}
public RowKeyDistributorByHashPrefix(Hasher hasher) {
this.hasher = hasher;
}
/**
* todo
*/
public interface Hasher extends Parametrizable {
byte[] getHashPrefix(byte[] originalKey);
byte[][] getAllPossiblePrefixes();
int getPrefixLength(byte[] adjustedKey);
}
/**
* todo
*/
public static class OneByteSimpleHash implements Hasher {
private int mod;
/**
* For reflection, do NOT use it.
*/
public OneByteSimpleHash() {}
/**
* Creates a new instance of this class.
* @param maxBuckets max buckets number, should be in 1...255 range
*/
public OneByteSimpleHash(int maxBuckets) {
if (maxBuckets < 1 || maxBuckets > 256) {
throw new IllegalArgumentException("maxBuckets should be in 1..256 range");
}
// i.e. "real" maxBuckets value = maxBuckets or maxBuckets-1
this.mod = maxBuckets;
}
// Used to minimize # of created object instances
// Should not be changed. TODO: secure that
private static final byte[][] PREFIXES;
static {
PREFIXES = new byte[256][];
for (int i = 0; i < 256; i++) {
PREFIXES[i] = new byte[] {(byte) i};
}
}
@Override
public byte[] getHashPrefix(byte[] originalKey) {
long hash = Math.abs(Arrays.hashCode(originalKey));
return new byte[] {(byte) (hash % mod)};
}
@Override
public byte[][] getAllPossiblePrefixes() {
return Arrays.copyOfRange(PREFIXES, 0, mod);
}
@Override
public int getPrefixLength(byte[] adjustedKey) {
return 1;
}
@Override
public String getParamsToStore() {
return String.valueOf(mod);
}
@Override
public void init(String storedParams) {
this.mod = Integer.valueOf(storedParams);
}
}
@Override
public byte[] getDistributedKey(byte[] originalKey) {
return Bytes.add(hasher.getHashPrefix(originalKey), originalKey);
}
@Override
public byte[] getOriginalKey(byte[] adjustedKey) {
int prefixLength = hasher.getPrefixLength(adjustedKey);
if (prefixLength > 0) {
return Bytes.tail(adjustedKey, adjustedKey.length - prefixLength);
} else {
return adjustedKey;
}
}
@Override
public byte[][] getAllDistributedKeys(byte[] originalKey) {
byte[][] allPrefixes = hasher.getAllPossiblePrefixes();
byte[][] keys = new byte[allPrefixes.length][];
for (int i = 0; i < allPrefixes.length; i++) {
keys[i] = Bytes.add(allPrefixes[i], originalKey);
}
return keys;
}
@Override
public String getParamsToStore() {
String hasherParamsToStore = hasher.getParamsToStore();
return hasher.getClass().getName() + DELIM + (hasherParamsToStore == null ? "" : hasherParamsToStore);
}
@Override
public void init(String params) {
String[] parts = params.split(DELIM, 2);
try {
this.hasher = (Hasher) Class.forName(parts[0]).newInstance();
this.hasher.init(parts[1]);
} catch (Exception e) {
throw new RuntimeException("RoKeyDistributor initialization failed", e);
}
}
}