/** * (c) Copyright 2012 WibiData, Inc. * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * 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 org.kiji.mapreduce.kvstore.framework; import java.util.Map.Entry; import com.google.common.base.Preconditions; import org.apache.hadoop.conf.Configuration; import org.kiji.annotations.ApiAudience; import org.kiji.annotations.ApiStability; /** * Used to serialize KeyValueStore information into a unique namespace * inside a Configuration object. All conf keys written to this object (e.g., "foo") * are prefixed by a namespace ("ns") specific to the particular key-value store * instance (e.g., "ns.foo"). */ @ApiAudience.Framework @ApiStability.Evolving public final class KeyValueStoreConfiguration { /** The parent Configuration to write to. */ private final Configuration mDelegate; /** The namespace to write in. */ private final String mNamespace; /** * KeyValueStore definitions are serialized to the Configuration as a set of * keys under the "kiji.kvstores.[i]" namespace. */ public static final String KEY_VALUE_STORE_NAMESPACE = "kiji.job.kvstores."; /** * Factory method to wrap a Configuration in a KeyValueStoreConfiguration. * * <p>The resulting KeyValueStoreConfiguration will have all key-value pairs * that the Configuration does, stored as part of the "0"'th KeyValueStore * entry in the "kiji.job.kvstores" namespace in a new Configuration object * (e.g., calling <code>set("foo", ...)</code> on the returned object will * set key <tt>kiji.job.kvstores.0.foo</tt>). * </p> * * <p>Since this KeyValueStoreConfiguration will not be part of an existing * Configuration, this method is mostly useful for writing tests or creating * temporary key-value stores to merge into a Configuration later.</p> * * @param conf the Configuration to wrap in a KeyValueStoreConfiguration. * @return A new KeyValueStoreConfiguration containing the keys of the conf * conf parameter. It will be backed by a new Configuration without loaded * defaults. */ public static KeyValueStoreConfiguration fromConf(Configuration conf) { KeyValueStoreConfiguration theConf = new KeyValueStoreConfiguration( new Configuration(false), 0); for (Entry<String, String> e : conf) { theConf.set(e.getKey(), e.getValue()); } return theConf; } /** * Factory method to create a KeyValueStoreConfiguration that is backed by a * supplied Configuration. * * <p>The KeyValueStoreConfiguration will be created in the KeyValueStore namespace * at the supplied storeIndex. For example if <code>storeIndex</code> is 3, then * calling calling <code>set("foo", ...)</code> on the returned object will * set key <tt>kiji.job.kvstores.3.foo</tt> in <code>conf</code>.</p> * * <p>If the Configuration contains keys set in the <code>storeIndex</code>th * namespace, they will be reflected in the returned KeyValueStoreConfiguration.</p> * * @param conf The Configuration that will back the KeyValueStoreConfiguration. * @param storeIndex The namespace index to write to. Must be non-negative. * @return A KeyValueStoreConfiguration backed by <code>conf</code>. */ public static KeyValueStoreConfiguration createInConfiguration( Configuration conf, int storeIndex) { Preconditions.checkArgument(storeIndex >= 0, "storeIndex must be non-negative."); return new KeyValueStoreConfiguration(conf, storeIndex); } /** * Helper function to generate the Configuration key that would be used for a key * in a KeyValueStoreConfiguration stored at a particular index. Useful for testing. * * @param key The key as it would be passed to a hypothetical * KeyValueStoreConfiguration. * @param storeIndex The index of the hypothetical KeyValueStoreConfiguration. * @return The key that would be used for a backing Configuration. */ public static String confKeyAtIndex(String key, int storeIndex) { return KEY_VALUE_STORE_NAMESPACE + storeIndex + "." + key; } /** * Creates a KeyValueStoreConfiguration that writes to the <code>storeIndex</code>th * KeyValueStore namespace. * * @param parent The parent Configuration to back data. * @param storeIndex The namespace index to write to. */ private KeyValueStoreConfiguration(Configuration parent, int storeIndex) { this(parent, KEY_VALUE_STORE_NAMESPACE + storeIndex); } /** * Constructs a new KeyValueStoreConfiguration that will read and write * values to the parent Configuration, under an arbitrary namespace. * * @param parent The Configuration to back this KeyValueStoreConfiguration. * @param namespace The namespace to write to. */ private KeyValueStoreConfiguration(Configuration parent, String namespace) { if (null == parent || null == namespace) { throw new IllegalArgumentException("Parent configuration and namespace must be non-null."); } mDelegate = parent; mNamespace = namespace; } /** * Returns the parent Configuration that backs this KeyValueStoreConfiguration. * * @return The parent Configuration. */ public Configuration getDelegate() { return mDelegate; } /** * Returns the namespace that this KeyValueStoreConfiguration works in. * * @return The namespace. */ public String getNamespace() { return mNamespace; } /** * Get the value of the <code>name</code> property, or null * if no such property exists. * * @param name The property to get a value for. * @return The value, or null, if no such property exists. */ public String get(String name) { return mDelegate.get(prefix(name)); } /** * Get the value of the <code>name</code> property. * * @param name The property to get a value for. * @param defaultValue The default value if none is specified. * @return The value. */ public String get(String name, String defaultValue) { return mDelegate.get(prefix(name), defaultValue); } /** * Sets the <code>value</code> of the <code>name</code> property. * * @param name The property to set. * @param value The value to set. */ public void set(String name, String value) { mDelegate.set(prefix(name), value); } /** * Get the value of the <code>name</code> property as a boolean. * * @param name The property to get a boolean for. * @param defaultValue The value to return if it is not set. * @return The value of this property as a boolean. */ public boolean getBoolean(String name, boolean defaultValue) { return mDelegate.getBoolean(prefix(name), defaultValue); } /** * Sets the value of the <code>name</code> property to a boolean. * * @param name The property to set. * @param value The boolean to set. */ public void setBoolean(String name, boolean value) { mDelegate.setBoolean(prefix(name), value); } /** * Gets the value of the <code>name</code> property as a Class. * * @param name The property to get a Class for. * @param defaultValue The value to return if it is not set. * @return The value of this property as a Class. */ public Class<?> getClass(String name, Class<?> defaultValue) { return mDelegate.getClass(prefix(name), defaultValue); } /** * Get the value of the <code>name</code> property as a Class implementing the * interface specified by <code>xface</code>. If no such property is specified, * then <code>defaultValue</code> is returned. An exception is thrown if the * returned class does not implement the named interface. * * @param <U> The interface to deserialize this class to. * @param name The property to get a Class for. * @param defaultValue The value to return if it is not set. * @param xface The interface implemented by the named class. * @return The value of this property as a Class implementing <code>xface</code>. */ public <U> Class<? extends U> getClass(String name, Class<? extends U> defaultValue, Class<U> xface) { return mDelegate.getClass(prefix(name), defaultValue, xface); } /** * Gets the value of the <code>name</code> property as a Class. * * @param name The property to get a Class for. * @return The value of this property as a Class. * @throws ClassNotFoundException If the class is not found. */ public Class<?> getClassByName(String name) throws ClassNotFoundException { return mDelegate.getClassByName(prefix(name)); } /** * Sets the value of the <code>name</code> property to be a Class. * * @param name The property to set. * @param theClass The Class to set. * @param xface The interface implemented by the named class. */ public void setClass(String name, Class<?> theClass, Class<?> xface) { mDelegate.setClass(prefix(name), theClass, xface); } /** * Gets the value of the <code>name</code> property as a float. * * @param name The property to get a float for. * @param defaultValue The value to return if it is not set. * @return The value of this property as a float. */ public float getFloat(String name, float defaultValue) { return mDelegate.getFloat(prefix(name), defaultValue); } /** * Sets the value of the <code>name</code> property to be a float. * * @param name The property to set. * @param value The float to set. */ public void setFloat(String name, float value) { mDelegate.setFloat(prefix(name), value); } /** * Gets the value of the <code>name</code> property as an int. * * @param name The property to get an int for. * @param defaultValue The value to return if it is not set. * @return The value of this property as an int. */ public int getInt(String name, int defaultValue) { return mDelegate.getInt(prefix(name), defaultValue); } /** * Sets the value of the <code>name</code> property to be an int. * * @param name The property to set. * @param value The int to set. */ public void setInt(String name, int value) { mDelegate.setInt(prefix(name), value); } /** * Gets the value of the <code>name</code> property as a long. * * @param name The property to get a long for. * @param defaultValue The value to return if it is not set. * @return The value of this property as a long. */ public long getLong(String name, long defaultValue) { return mDelegate.getLong(prefix(name), defaultValue); } /** * Sets the value of the <code>name</code> property to be a long. * * @param name The property to set. * @param value The long to set. */ public void setLong(String name, long value) { mDelegate.setLong(prefix(name), value); } /** * Get the comma delimited values of the <code>name</code> property * as an array of <code>String</code>s. * * @param name The property to get a String array for. * @return The value of this property as a String array. */ public String[] getStrings(String name) { return mDelegate.getStrings(prefix(name)); } /** * Get the comma delimited values of the <code>name</code> property * as an array of <code>String</code>s. If none found, returns * the default value instead. * * @param name The property to get a String array for. * @param defaultValue The value to return if it is not set. * @return The value of this property as a String array. */ public String[] getStrings(String name, String... defaultValue) { return mDelegate.getStrings(prefix(name), defaultValue); } /** * Set the array of string values for the <code>name</code> property as comma * delimited values. * * @param name The property to set. * @param values The values. */ public void setStrings(String name, String... values) { mDelegate.setStrings(prefix(name), values); } /** * Get the value of the <code>name</code> property, without doing variable expansion. * * @param name The property to get a raw value for. * @return The raw value. */ public String getRaw(String name) { return mDelegate.getRaw(prefix(name)); } /** * Returns the specified name, prepended with the namespace prefix for all keys * stored in this KeyValueStoreConfiguration. * * @param name The String to prefix with the namespace. * @return The true key to write into the underlying Configuration; the namespace * concatenated with a "." character and the specified name. */ private String prefix(String name) { return getNamespace() + "." + name; } }