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

org.javabits.yar.guice.CacheContainer Maven / Gradle / Ivy

There is a newer version: 3.0.1.RELEASE
Show newest version
/*
 * Copyright 2013 Romain Gilles
 *
 *    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.javabits.yar.guice;

import com.google.common.base.Function;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;

import javax.annotation.Nullable;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;

import static com.google.common.base.Throwables.propagate;
import static java.util.Collections.unmodifiableList;
import static org.javabits.yar.guice.CacheContainer.KeyConversionStrategies.NO_TYPE_ERASURE;
import static org.javabits.yar.guice.KeyEvent.newKeyEvent;

/**
 * This class is responsible to maintain a multi-map of {@link Type}s
 * associated to values. It's implementation is based on Guava {@code LoadingCache}.
 * {@link KeyListener}{@literal } can be added. They will be
 * triggered on {@code CacheLoader.load(Type)} for addition, and on
 * {@link #invalidate(java.lang.reflect.Type)}, {@link #invalidateAll(Iterable)}
 * for removal.
 *
 * @param  the type of the multi-values associated to a {@link Type}
 *
 * @author Romain Gilles
 */
class CacheContainer implements Container {


    private final LoadingCache> loadingCache;
    private final Collection> keyListeners;
    private final Function keyConversionStrategy;

    enum KeyConversionStrategies implements Function {
        NO_TYPE_ERASURE() {
            @Nullable
            @Override
            public Type apply(@Nullable Type type) {
                return type;
            }
        },
        TYPE_ERASURE() {
            @Nullable
            @Override
            public Type apply(@Nullable Type type) {
                return Reflections.getRawType(type);
            }
        }
    }


    static  CacheContainer newConcurrentContainer() {
        return newConcurrentContainer(new CopyOnWriteArrayList>(), NO_TYPE_ERASURE);
    }

    private static  CacheContainer newConcurrentContainer(final Collection> typeListeners, Function keyConversionStrategy) {
        return new CacheContainer<>(CacheBuilder.newBuilder().build(new CacheLoader>() {
            @Override
            public List load(Type key) {
                for (KeyListener typeListener : typeListeners) {
                    typeListener.keyAdded(newKeyEvent(key));
                }
                return new CopyOnWriteArrayList<>();
            }
        }), typeListeners, keyConversionStrategy);
    }

    static  CacheContainer newNonConcurrentContainer(Function keyConversionStrategy) {
        return new CacheContainer<>(keyConversionStrategy);
    }


    private CacheContainer(Function keyConversionStrategy) {
        final CopyOnWriteArrayList> keyListeners = new CopyOnWriteArrayList<>();
        this.keyListeners = keyListeners;
        this.loadingCache = CacheBuilder.newBuilder().build(new CacheLoader>() {
            @Override
            public List load(Type key) {
                for (KeyListener keyListener : keyListeners) {
                    keyListener.keyAdded(newKeyEvent(key));
                }
                return new CopyOnWriteArrayList<>();
            }
        });
        this.keyConversionStrategy = keyConversionStrategy;
    }

    private CacheContainer(LoadingCache> loadingCache, Collection> keyListeners, Function keyConversionStrategy) {
        this.loadingCache = loadingCache;
        this.keyListeners = keyListeners;
        this.keyConversionStrategy = keyConversionStrategy;
    }

    @Override
    public List getAll(Type key) {
        try {
            key = keyConversionStrategy.apply(key);
            return unmodifiableList(loadingCache.get(key));
        } catch (ExecutionException e) {
            throw propagate(e);
        }
    }

    @Nullable
    @Override
    public V getFirst(Type key) {
        key = keyConversionStrategy.apply(key);
        return Iterables.getFirst(getAll(key), null);
    }

    @Override
    public boolean put(Type key, V value) {
        key = keyConversionStrategy.apply(key);
        try {
            List valueList = loadingCache.get(key);
            return valueList.add(value);
        } catch (ExecutionException e) {
            throw propagate(e);
        }
    }

    @Override
    public boolean remove(Type key, V value) {
        key = keyConversionStrategy.apply(key);
        try {
            return loadingCache.get(key).remove(value);
        } catch (ExecutionException e) {
            throw propagate(e);
        }
    }

    @Override
    public void invalidate(Type key) {
        key = keyConversionStrategy.apply(key);
        loadingCache.invalidate(key);
        fireKeyRemoved(key);

    }

    private void fireKeyRemoved(Type key) {
        for (KeyListener keyListener : keyListeners) {
            keyListener.keyRemoved(newKeyEvent(key));
        }
    }

    public void invalidateAll(Iterable keys) {
        loadingCache.invalidateAll(keys);
        for (Type key : keys) {
            key = keyConversionStrategy.apply(key);
            fireKeyRemoved(key);
        }
    }

    @Override
    public Map> asMap() {
        return loadingCache.asMap();
    }

    @Override
    public void addKeyListener(KeyListener keyListener) {
        keyListeners.add(keyListener);
    }

    @Override
    public void removeKeyListener(KeyListener keyListener) {
        keyListeners.add(keyListener);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy