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

com.palantir.giraffe.file.base.SystemCache Maven / Gradle / Ivy

/**
 * Copyright 2015 Palantir Technologies, 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 com.palantir.giraffe.file.base;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;

import com.google.common.util.concurrent.AbstractFuture;

/**
 * A thread-safe cache for file systems or similar I/O systems. Systems are
 * initialized and cached atomically. Threads that try to get a system while it
 * is being constructed block until construction is complete or an error occurs.
 *
 * @author bkeyes
 *
 * @param  the type of cache keys
 * @param  the type of cached systems
 */
public final class SystemCache {

    /**
     * Constructs new systems to be stored in the cache.
     *
     * @param  the type of system to construct
     */
    public interface Factory {
        S newSystem() throws IOException;
    }

    private static final class Reference extends AbstractFuture {
        private void init(S system) {
            set(system);
        }

        private void fail(Throwable t) {
            setException(t);
        }
    }

    private final ConcurrentHashMap> cache = new ConcurrentHashMap<>();

    /**
     * Gets the system associated with {@code key} if it exists. Returns
     * {@code null} if there is no system associated with {@code key}.
     * 

* If a thread calls this method concurrently with another thread that is * initializing a system for the same key, this thread will block until * initialization completes. If an error occurs in the initialization * thread, it is also reported to this thread. * * @throws InterruptedException if this thread is interrupted while waiting * for initialization * @throws ExecutionException if an error occurs while this thread is * waiting for initialization */ public S get(K key) throws InterruptedException, ExecutionException { Reference ref = cache.get(key); if (ref == null) { return null; } else { return ref.get(); } } /** * Atomically initializes a system associated with {@code key} if no such * system exists. If this thread initialized the system, the system is * returned. Otherwise, this method returns {@code null}. * * @throws IOException if an I/O error occurs while initializing the system */ public S init(K key, Factory factory) throws IOException { final Reference existing = cache.get(key); if (existing == null) { Reference ref = new Reference<>(); if (cache.putIfAbsent(key, ref) == null) { try { S system = factory.newSystem(); ref.init(system); return system; } catch (IOException e) { ref.fail(e); throw e; } } } return null; } /** * Removes the initialized system associated with {@code key} from the * cache. * * @throws IllegalStateException if this method is called while * initialization is in progress. */ public void remove(K key) { Reference ref = cache.get(key); if (ref.isDone()) { cache.remove(key, ref); } else { throw new IllegalStateException("system is not initialized, key = " + key); } } }