All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aerospike.client.Key Maven / Gradle / Ivy

There is a newer version: 0.2
Show newest version
/*******************************************************************************
 * Copyright 2012-2014 by Aerospike.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 ******************************************************************************/
package com.aerospike.client;

import gnu.crypto.hash.RipeMD160;

import java.util.Arrays;

import com.aerospike.client.command.Buffer;
import com.aerospike.client.command.ParticleType;
import com.aerospike.client.util.ThreadLocalData;

/**
 * Unique record identifier. Records can be identified using a specified namespace,
 * an optional set name, and a user defined key which must be unique within a set.
 * Records can also be identified by namespace/digest which is the combination used 
 * on the server.
 */
public final class Key {
	/**
	 * Namespace. Equivalent to database name.
	 */
	public final String namespace;
	
	/**
	 * Optional set name. Equivalent to database table.
	 */
	public final String setName;
	
	/**
	 * Unique server hash value generated from set name and user key.
	 */
	public final byte[] digest;
	
	/**
	 * Original user key. This key is immediately converted to a hash digest.
	 * This key is not used or returned by the server.  If the user key needs 
	 * to persist on the server, the key should be explicitly stored in a bin.
	 */
	public final Object userKey;
	
	/**
	 * Initialize key from namespace, optional set name and user key.
	 * The set name and user defined key are converted to a digest before sending to the server.
	 * The server handles record identifiers by digest only.
	 * 
	 * @param namespace				namespace
	 * @param setName				optional set name, enter null when set does not exist
	 * @param key					user defined unique identifier within set.
	 * @throws AerospikeException	if digest computation fails
	 */
	public Key(String namespace, String setName, String key) throws AerospikeException {
		this.namespace = namespace; 
		this.setName = setName;
		this.userKey = key;
		digest = computeDigest(setName, new Value.StringValue(key));
	}

	/**
	 * Initialize key from namespace, optional set name and user key.
	 * The set name and user defined key are converted to a digest before sending to the server.
	 * The server handles record identifiers by digest only.
	 * If the user key needs to be stored on the server, the key should be stored in a bin.
	 * 
	 * @param namespace				namespace
	 * @param setName				optional set name, enter null when set does not exist
	 * @param key					user defined unique identifier within set.
	 * @throws AerospikeException	if digest computation fails
	 */
	public Key(String namespace, String setName, byte[] key) throws AerospikeException {
		this.namespace = namespace; 
		this.setName = setName;
		this.userKey = key;
		digest = computeDigest(setName, new Value.BytesValue(key));
	}

	/**
	 * Initialize key from namespace, optional set name and user key.
	 * The set name and user defined key are converted to a digest before sending to the server.
	 * The server handles record identifiers by digest only.
	 * If the user key needs to be stored on the server, the key should be stored in a bin.
	 * 
	 * @param namespace				namespace
	 * @param setName				optional set name, enter null when set does not exist
	 * @param key					user defined unique identifier within set.
	 * @param offset				byte array segment offset
	 * @param length				byte array segment length
	 * @throws AerospikeException	if digest computation fails
	 */
	public Key(String namespace, String setName, byte[] key, int offset, int length) throws AerospikeException {
		this.namespace = namespace; 
		this.setName = setName;
		Value value = new Value.ByteSegmentValue(key, offset, length);
		this.userKey = value;
		digest = computeDigest(setName, value);
	}

	/**
	 * Initialize key from namespace, optional set name and user key.
	 * The set name and user defined key are converted to a digest before sending to the server.
	 * The server handles record identifiers by digest only.
	 * If the user key needs to be stored on the server, the key should be stored in a bin.
	 * 
	 * @param namespace				namespace
	 * @param setName				optional set name, enter null when set does not exist
	 * @param key					user defined unique identifier within set.
	 * @throws AerospikeException	if digest computation fails
	 */
	public Key(String namespace, String setName, int key) throws AerospikeException {
		this.namespace = namespace; 
		this.setName = setName;
		this.userKey = key;
		digest = computeDigest(setName, new Value.LongValue(key));
	}

	/**
	 * Initialize key from namespace, optional set name and user key.
	 * The set name and user defined key are converted to a digest before sending to the server.
	 * The server handles record identifiers by digest only.
	 * If the user key needs to be stored on the server, the key should be stored in a bin.
	 * 
	 * @param namespace				namespace
	 * @param setName				optional set name, enter null when set does not exist
	 * @param key					user defined unique identifier within set.
	 * @throws AerospikeException	if digest computation fails
	 */
	public Key(String namespace, String setName, long key) throws AerospikeException {
		this.namespace = namespace; 
		this.setName = setName;
		this.userKey = key;
		digest = computeDigest(setName, new Value.LongValue(key));
	}

	/**
	 * Initialize key from namespace, optional set name and user key.
	 * The set name and user defined key are converted to a digest before sending to the server.
	 * The server handles record identifiers by digest only.
	 * If the user key needs to be stored on the server, the key should be stored in a bin.
	 * 
	 * @param namespace				namespace
	 * @param setName				optional set name, enter null when set does not exist
	 * @param key					user defined unique identifier within set.
	 * @throws AerospikeException	if digest computation fails
	 */
	public Key(String namespace, String setName, Value key) throws AerospikeException {
		this.namespace = namespace; 
		this.setName = setName;
		this.userKey = key;
		digest = computeDigest(setName, key);
	}

	/*
	 * Removed Object constructor because the type must be determined using multiple "instanceof"
	 * checks.  If the type is not known, java serialization (slow) is used for byte conversion.
	 * These two performance penalties make this constructor unsuitable in all cases from
	 * a performance perspective.
	 * 
	 * The preferred method when using compound java key objects is to explicitly convert the 
	 * object to a byte[], String (or other known type) and call the associated Key constructor.
	 * 
	public Key(String namespace, String setName, Object key) throws AerospikeException {
		this.namespace = namespace; 
		this.setName = setName;
		this.userKey = key;
		digest = computeDigest(setName, Value.get(key));
	} */
	
	/**
	 * Initialize key from namespace, digest and optional set name.
	 * 
	 * @param namespace				namespace
	 * @param digest				unique server hash value
	 * @param setName				optional set name, enter null when set does not exist
	 */
	public Key(String namespace, byte[] digest, String setName) {
		this.namespace = namespace; 
		this.digest = digest;
		this.setName = setName;
		this.userKey = null;
	}
	
	/**
	 * Initialize key from namespace and digest.
	 * This constructor exists for the legacy CitrusleafClient compatibility layer only.
	 * Do not use.
	 * 
	 * @param namespace				namespace
	 * @param digest				unique server hash value
	 */
	public Key(String namespace, byte[] digest) {
		this.namespace = namespace; 
		this.digest = digest;
		this.setName = null;
		this.userKey = null;
	}

	/**
	 * Hash lookup uses namespace and digest.
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = prime + Arrays.hashCode(digest);
		return prime * result + namespace.hashCode();
	}

	/**
	 * Equality uses namespace and digest.
	 */
	@Override
	public boolean equals(Object obj) {
		Key other = (Key) obj;
		
		if (! Arrays.equals(digest, other.digest))
			return false;
		
		return namespace.equals(other.namespace);
	}

	/**
	 * Generate unique server hash value from set name, key type and user defined key.  
	 * The hash function is RIPEMD-160 (a 160 bit hash).
	 * 
	 * @param setName				optional set name, enter null when set does not exist
	 * @param key					record identifier, unique within set
	 * @return						unique server hash value
	 * @throws AerospikeException	if digest computation fails
	 */
	public static byte[] computeDigest(String setName, Value key) throws AerospikeException {
		int keyType = key.getType();
		
		if (keyType == ParticleType.NULL) {
			throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key: null");
		}
		
		// This method runs 14% faster using thread local byte array 
		// versus creating the buffer each time.
		byte[] buffer = ThreadLocalData.getBuffer();
		int setLength = Buffer.stringToUtf8(setName, buffer, 0);

		buffer[setLength] = (byte)keyType;		
		int keyLength = key.write(buffer, setLength + 1);

		// Run additional 16% faster using direct class versus 
		// HashFactory.getInstance("RIPEMD-160").
		RipeMD160 hash = new RipeMD160();
		hash.update(buffer, 0, setLength);
		hash.update(buffer, setLength, keyLength + 1);
		return hash.digest();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy