net.derquinse.bocas.AbstractGuavaCachingBocas.java Source code

Java tutorial

Introduction

Here is the source code for net.derquinse.bocas.AbstractGuavaCachingBocas.java

Source

/*
 * Copyright (C) the original author or authors.
 *
 * 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 net.derquinse.bocas;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

import net.derquinse.common.base.ByteString;
import net.derquinse.common.io.MemoryByteSource;
import net.derquinse.common.io.MemoryByteSourceLoader;

import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.ByteSource;
import com.google.common.util.concurrent.UncheckedExecutionException;

/**
 * Abstract base class for Guava-cache-based bocas buckets.
 * @author Andres Rodriguez.
 */
abstract class AbstractGuavaCachingBocas<K> extends AbstractMemoryBocas {
    /** Cached bucket. */
    private final Bocas bocas;
    /** Entry Cache. */
    private final Cache<K, MemoryByteSource> cache;
    /** Whether writes are always performed. */
    private final boolean alwaysWrite;

    /** Constructor. */
    AbstractGuavaCachingBocas(Bocas bocas, MemoryByteSourceLoader loader, Cache<K, MemoryByteSource> cache,
            boolean alwaysWrite) {
        super(checkNotNull(bocas, "The bucket to cache must be provided").getHashFunction(), loader);
        this.bocas = bocas;
        this.cache = checkNotNull(cache, "The cache to use must be provided");
        this.alwaysWrite = alwaysWrite;
    }

    final MemoryByteSource loadFromSource(ByteString key) {
        Optional<ByteSource> v = bocas.get(key);
        if (v.isPresent()) {
            return transform(v.get());
        }
        throw new EntryNotFoundException();
    }

    abstract Callable<MemoryByteSource> getLoader(K internalKey);

    abstract K toInternalKey(ByteString key);

    abstract ByteString toKey(K internalKey);

    abstract Set<K> toInternalKeySet(Iterable<ByteString> keys);

    abstract Set<ByteString> toKeySet(Set<K> internalKeys);

    abstract Map<K, MemoryByteSource> toInternalEntryMap(Map<ByteString, MemoryByteSource> entries);

    /*
     * (non-Javadoc)
     * @see net.derquinse.bocas.Bocas#close()
     */
    @Override
    public void close() {
        // Nothing to do.
    }

    /*
     * (non-Javadoc)
     * @see net.derquinse.bocas.Bocas#contains(net.derquinse.common.base.ByteString)
     */
    @Override
    public final boolean contains(ByteString key) {
        final K internalKey = toInternalKey(key);
        if (cache.asMap().containsKey(internalKey)) {
            return true;
        }
        return bocas.contains(key);
    }

    /*
     * (non-Javadoc)
     * @see net.derquinse.bocas.Bocas#contained(java.lang.Iterable)
     */
    @Override
    public final Set<ByteString> contained(Iterable<ByteString> keys) {
        Set<K> ikRequested = toInternalKeySet(keys);
        if (ikRequested.isEmpty()) {
            return ImmutableSet.of();
        }
        Set<K> ikCached = Sets.intersection(ikRequested, cache.asMap().keySet()).immutableCopy();
        Set<K> ikNotCached = Sets.difference(ikRequested, ikCached).immutableCopy();
        Set<ByteString> kCached = toKeySet(ikCached);
        if (ikNotCached.isEmpty()) {
            return kCached;
        }
        Set<ByteString> kFound = bocas.contained(toKeySet(ikNotCached));
        return Sets.union(kCached, kFound).immutableCopy();
    }

    /*
     * (non-Javadoc)
     * @see net.derquinse.bocas.Bocas#get(net.derquinse.common.base.ByteString)
     */
    @Override
    public final Optional<ByteSource> get(ByteString key) {
        try {
            K internalKey = toInternalKey(key);
            ByteSource v = cache.get(internalKey, getLoader(internalKey));
            return Optional.of(v);
        } catch (UncheckedExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof EntryNotFoundException) {
                return Optional.absent();
            }
            if (cause instanceof BocasException) {
                throw (BocasException) cause;
            }
            throw new BocasException(cause);
        } catch (ExecutionException e) {
            throw new BocasException(e.getCause());
        }
    }

    /*
     * (non-Javadoc)
     * @see net.derquinse.bocas.Bocas#get(java.lang.Iterable)
     */
    @Override
    public final Map<ByteString, ByteSource> get(Iterable<ByteString> keys) {
        Set<K> ikRequested = toInternalKeySet(keys);
        if (ikRequested.isEmpty()) {
            return ImmutableMap.of();
        }
        Map<ByteString, ByteSource> found = Maps.newHashMapWithExpectedSize(ikRequested.size());
        Set<ByteString> notCached = Sets.newHashSetWithExpectedSize(ikRequested.size());
        for (K internalKey : ikRequested) {
            ByteString key = toKey(internalKey);
            ByteSource value = cache.getIfPresent(internalKey);
            if (value != null) {
                found.put(key, value);
            } else {
                notCached.add(key);
            }
        }
        Map<ByteString, ByteSource> foundNotCached = bocas.get(notCached);
        for (Entry<ByteString, ByteSource> e : foundNotCached.entrySet()) {
            ByteString key = e.getKey();
            K internalKey = toInternalKey(key);
            MemoryByteSource value = transform(e.getValue());
            cache.put(internalKey, value);
            found.put(key, value);
        }
        return found;
    }

    /*
     * (non-Javadoc)
     * @see net.derquinse.bocas.SkeletalBocas#put(net.derquinse.common.base.ByteString,
     * com.google.common.io.ByteSource)
     */
    @Override
    protected void put(ByteString key, MemoryByteSource value) {
        if (alwaysWrite) {
            bocas.put(value);
        }
        K internalKey = toInternalKey(key);
        if (!cache.asMap().containsKey(internalKey)) {
            if (!alwaysWrite) {
                // otherwise, already written
                bocas.put(value);
            }
            cache.put(internalKey, value);
        }
    }

    /*
     * (non-Javadoc)
     * @see net.derquinse.bocas.SkeletalBocas#putAll(java.util.Map)
     */
    @Override
    protected void putAll(Map<ByteString, MemoryByteSource> entries) {
        if (alwaysWrite) {
            bocas.putAll(entries.values());
        }
        final Map<K, MemoryByteSource> map = cache.asMap();
        final Map<K, MemoryByteSource> notCached = Maps.filterKeys(toInternalEntryMap(entries),
                Predicates.not(Predicates.in(map.keySet())));
        if (notCached.isEmpty()) {
            return;
        }
        if (!alwaysWrite) {
            bocas.putAll(notCached.values());
        }
        map.putAll(notCached);
    }
}