org.kiji.mapreduce.kvstore.KeyValueStoreReaderFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.kiji.mapreduce.kvstore.KeyValueStoreReaderFactory.java

Source

/**
 * (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;

import java.io.Closeable;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import com.google.common.collect.Maps;

import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.util.ReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.kiji.annotations.ApiAudience;
import org.kiji.annotations.ApiStability;
import org.kiji.mapreduce.kvstore.framework.KeyValueStoreConfiguration;
import org.kiji.mapreduce.kvstore.impl.KeyValueStoreConfigSerializer;

/**
 * Class that manages the creation of KeyValueStoreReaders associated
 * with a set of bound KeyValueStore name--instance pairs.
 *
 * <p>This also manages a cache of opened readers, which will be returned
 * if available, rather than creating a new store reader for a given named
 * store.</p>
 *
 * <p>The {@link #close()} method of this object will close all KeyValueStoreReaders
 * associated with it. You should call this when you are done with the
 * readers, or close them all individually.</p>
 */
@ApiAudience.Public
@ApiStability.Evolving
public final class KeyValueStoreReaderFactory implements Closeable {
    private static final Logger LOG = LoggerFactory.getLogger(KeyValueStoreReaderFactory.class.getName());

    /** The set of Key-value stores to provide to the KeyValueStoreClient. */
    private final Map<String, KeyValueStore<?, ?>> mKeyValueStores;

    /** A cache of opened key-value stores we may use and modify. */
    private final Map<String, KeyValueStoreReader<?, ?>> mKVStoreReaderCache;

    /** Creates an empty KeyValueStoreReaderFactory. */
    private KeyValueStoreReaderFactory() {
        this(Collections.<String, KeyValueStore<?, ?>>emptyMap());
    }

    /**
     * Creates a KeyValueStoreReaderFactory backed by a map of specific store bindings.
     *
     * @param storeBindings defines the set of KeyValueStores available, and the
     *     names by which they are registered.
     */
    private KeyValueStoreReaderFactory(Map<String, KeyValueStore<?, ?>> storeBindings) {
        mKeyValueStores = Collections.unmodifiableMap(new HashMap<String, KeyValueStore<?, ?>>(storeBindings));
        mKVStoreReaderCache = Maps.newConcurrentMap();
    }

    /**
     * Creates a KeyValueStoreReaderFactory backed by store bindings specified in a Configuration.
     *
     * @param conf the Configuration from which a set of KeyValueStore bindings should
     *     be deserialized and initialized.
     * @throws IOException if there is an error deserializing or initializing a
     *     KeyValueStore instance.
     */
    private KeyValueStoreReaderFactory(Configuration conf) throws IOException {
        Map<String, KeyValueStore<?, ?>> keyValueStores = new HashMap<String, KeyValueStore<?, ?>>();
        int numKvStores = conf.getInt(KeyValueStoreConfigSerializer.CONF_KEY_VALUE_STORE_COUNT,
                KeyValueStoreConfigSerializer.DEFAULT_KEY_VALUE_STORE_COUNT);
        for (int i = 0; i < numKvStores; i++) {
            KeyValueStoreConfiguration kvStoreConf = KeyValueStoreConfiguration.createInConfiguration(conf, i);

            Class<? extends KeyValueStore> kvStoreClass = kvStoreConf
                    .<KeyValueStore>getClass(KeyValueStoreConfigSerializer.CONF_CLASS, null, KeyValueStore.class);

            String kvStoreName = kvStoreConf.get(KeyValueStoreConfigSerializer.CONF_NAME, "");

            if (null != kvStoreClass) {
                KeyValueStore<?, ?> kvStore = ReflectionUtils.newInstance(kvStoreClass, conf);
                if (null != kvStore) {
                    kvStore.initFromConf(kvStoreConf);
                    if (kvStoreName.isEmpty()) {
                        LOG.warn("Deserialized KeyValueStore not bound to a name; ignoring.");
                        continue;
                    }
                    keyValueStores.put(kvStoreName, kvStore);
                }
            }
        }

        mKeyValueStores = Collections.unmodifiableMap(keyValueStores);
        mKVStoreReaderCache = Maps.newConcurrentMap();
    }

    /**
     * Creates an empty KeyValueStoreReaderFactory.
     *
     * @return a new, empty KeyValueStoreReaderFactory.
     */
    public static KeyValueStoreReaderFactory createEmpty() {
        return new KeyValueStoreReaderFactory();
    }

    /**
     * Creates a KeyValueStoreReaderFactory backed by a map of specific store bindings.
     *
     * @param storeBindings defines the set of KeyValueStores available, and the
     *     names by which they are registered.
     * @return a new KeyValueStoreReaderFactory backed by a copy of the specified storeBindings.
     */
    public static KeyValueStoreReaderFactory create(Map<String, KeyValueStore<?, ?>> storeBindings) {
        return new KeyValueStoreReaderFactory(storeBindings);
    }

    /**
     * Creates a KeyValueStoreReaderFactory backed by store bindings specified in a Configuration.
     *
     * @param conf the Configuration from which a set of KeyValueStore bindings should
     *     be deserialized and initialized.
     * @throws IOException if there is an error deserializing or initializing a
     *     KeyValueStore instance.
     * @return a new KeyValueStoreReaderFactory backed by the storeBindings specified in conf.
     */
    public static KeyValueStoreReaderFactory create(Configuration conf) throws IOException {
        return new KeyValueStoreReaderFactory(conf);
    }

    /**
     * Closes all KeyValueStoreReaders opened by this factory.
     */
    @Override
    public void close() {
        for (KeyValueStoreReader<?, ?> reader : mKVStoreReaderCache.values()) {
            if (null != reader && reader.isOpen()) {
                IOUtils.closeQuietly(reader);
            }
        }
    }

    /**
     * Opens a KeyValueStore associated with storeName for read-access.
     *
     * <p>Calling openStore() multiple times on the same name will reuse the same
     * reader unless it is closed.</p>
     *
     * <p>Note: this method is thread safe as long as readers are only being created
     * and opened - two threads requesting the same reader will receive the same one.
     * In a multi-threaded environment, a closed reader may still be returned. You
     * can prevent this by avoiding calling close() on individual readers returned
     * from this method. Instead, call close() on the KeyValueStoreReaderFactory when
     * you are done with all stores, and all readers will be closed.</p>
     *
     * @param <K> The key type for the KeyValueStore.
     * @param <V> The value type for the KeyValueStore.
     * @param storeName the name of the KeyValueStore to open.
     * @return A KeyValueStoreReader associated with this storeName, or null
     *     if there is no such KeyValueStore available.
     * @throws IOException if there is an error opening the underlying storage resource.
     */
    @SuppressWarnings("unchecked")
    public <K, V> KeyValueStoreReader<K, V> openStore(String storeName) throws IOException {
        KeyValueStore<K, V> store = (KeyValueStore<K, V>) mKeyValueStores.get(storeName);
        KeyValueStoreReader<?, ?> reader = mKVStoreReaderCache.get(storeName);
        if (null != store && (null == reader || !reader.isOpen())) {
            synchronized (store) {
                reader = mKVStoreReaderCache.get(storeName);
                if (null == reader || !reader.isOpen()) {
                    reader = store.open();
                    mKVStoreReaderCache.put(storeName, reader);
                }
            }
        }
        return (KeyValueStoreReader<K, V>) reader;
    }
}