/* * JBoss, Home of Professional Open Source * Copyright 2014, Red Hat, Inc., and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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.jboss.weld.util.cache; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import org.jboss.weld.util.LazyValueHolder; import org.jboss.weld.util.ValueHolder; /** * A {@link ComputingCache} backed by a {@link ConcurrentHashMap} which intentionally does not use {@link Map#computeIfAbsent(Object, Function)} * and is reentrant. * * @author Jozef Hartinger * * @param <K> the key type * @param <V> the value type * @see ValueHolder * @see LazyValueHolder */ class ReentrantMapBackedComputingCache<K, V> implements ComputingCache<K, V>, Iterable<V> { private final ConcurrentMap<K, ValueHolder<V>> map; private final Long maxSize; private final Function<K, ValueHolder<V>> function; ReentrantMapBackedComputingCache(Function<K, V> computingFunction, Long maxSize) { this(computingFunction, LazyValueHolder::forSupplier, maxSize); } ReentrantMapBackedComputingCache(Function<K, V> computingFunction, Function<Supplier<V>, ValueHolder<V>> valueHolderFunction, Long maxSize) { this.map = new ConcurrentHashMap<>(); this.maxSize = maxSize; this.function = (key) -> valueHolderFunction.apply(() -> computingFunction.apply(key)); } @Override public V getValue(final K key) { ValueHolder<V> value = map.get(key); if (value == null) { value = function.apply(key); ValueHolder<V> previous = map.putIfAbsent(key, value); if (previous != null) { value = previous; } // finally, check that we are not over the bound if (maxSize != null && size() > maxSize) { clear(); } } return value.get(); } @SuppressWarnings("unchecked") @Override public <T> T getCastValue(Object key) { return (T) getValue((K) key); } @Override public V getValueIfPresent(K key) { ValueHolder<V> value = map.get(key); if (value == null) { return null; } return value.getIfPresent(); } @Override public long size() { return map.size(); } @Override public void clear() { map.clear(); } @Override public void invalidate(Object key) { map.remove(key); } @Override public Iterable<V> getAllPresentValues() { return this; } @Override public String toString() { return map.toString(); } @Override public void forEachValue(Consumer<? super V> consumer) { for (ValueHolder<V> valueHolder : map.values()) { V value = valueHolder.getIfPresent(); if (value != null) { consumer.accept(value); } } } @Override public Iterator<V> iterator() { return new Iterator<V>() { private final Iterator<ValueHolder<V>> delegate = map.values().iterator(); private V next = findNext(); @Override public boolean hasNext() { return next != null; } private V findNext() { while (delegate.hasNext()) { V next = delegate.next().getIfPresent(); if (next != null) { return next; } } return null; } @Override public V next() { if (!hasNext()) { throw new NoSuchElementException(); } V current = next; this.next = findNext(); return current; } }; } }