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

org.killbill.commons.utils.cache.DefaultCache Maven / Gradle / Ivy

/*
 * Copyright 2020-2022 Equinix, Inc
 * Copyright 2014-2022 The Billing Project, LLC
 *
 * The Billing Project licenses this file to you 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.killbill.commons.utils.cache;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;

import org.killbill.commons.utils.Preconditions;
import org.killbill.commons.utils.annotation.VisibleForTesting;

/**
 * 

* Default {@link Cache} implementation, that provide ability to: *

    *
  1. Add maxSize to the cache. If more entry added, the oldest entry get removed automatically
  2. *
  3. * Add lazy-loading capability with {@code cacheLoader} parameter in constructor. This {@code cacheLoader} * will be called if {@link #get(Object)} return {@code null}. {@code cacheLoader} also will take precedence * over loader defined in {@link #getOrLoad(Object, Function)}. *
  4. *
  5. Add timout (similar to expire-after-write in Guava and Caffeine) capability
  6. *
*

* @param cache key * @param cache value */ public class DefaultCache implements Cache { public static final long NO_TIMEOUT = 0; @VisibleForTesting final Map> map; private final long timeoutMillis; private final Function cacheLoader; /** * Create cache with maximum entry size, without any timout (live forever) and no cache loader. * * @param maxSize max entry that should be existed in cache. */ public DefaultCache(final int maxSize) { this(maxSize, NO_TIMEOUT, noCacheLoader()); } /** * Create cache with {@code maxSize = Integer.MAX_VALUE}, {@code timeoutInSecond = NO_TIMEOUT}, and with supplied * {@code cacheLoader}. * * @param cacheLoader cache loader. Use {@link #noCacheLoader()} to make this cache should not attempt to load * anything if value is null. */ public DefaultCache(final Function cacheLoader) { this(Integer.MAX_VALUE, NO_TIMEOUT, cacheLoader); } /** * Create cache with maximum entry size, timeout (in second), and cacheLoader capability. * * @param maxSize cache maximum size. If more entry added, the oldest entry get removed automatically. * @param timeoutInSecond cache entry expire time. Entry will eventually be removed after specifics time defined * here. Use {@link DefaultCache#NO_TIMEOUT} to make entry live forever. * @param cacheLoader cache loader. Use {@link #noCacheLoader()} to make this cache should not attempt to load * anything if value is null. */ public DefaultCache(final int maxSize, final long timeoutInSecond, final Function cacheLoader) { Preconditions.checkArgument(maxSize > 0, "cache maxSize should > 0"); Preconditions.checkArgument(timeoutInSecond >= 0, "cache timeoutInSecond should >= 0"); this.timeoutMillis = timeoutInSecond * 1_000; this.cacheLoader = Preconditions.checkNotNull(cacheLoader, "cacheLoader is null. Use DefaultCache#noCacheLoader() to create a cache without loader"); map = new LinkedHashMap<>(16, 0.75f, true) { @Override protected boolean removeEldestEntry(final Entry> eldest) { return size() > maxSize; } }; } /** * Create {@link DefaultCache} without any loader. */ public static Function noCacheLoader() { return k1 -> null; } protected boolean isTimeoutEnabled() { return timeoutMillis > 0L; } protected boolean isCacheLoaderExist() { return !noCacheLoader().equals(cacheLoader); } protected void evictExpireEntry(final K key) { if (isTimeoutEnabled()) { final TimedValue value = map.get(key); if (value != null && value.isTimeout()) { invalidate(key); } } } @Override public V get(final K key) { Preconditions.checkNotNull(key, "Cannot #get() cache with key = null"); evictExpireEntry(key); final TimedValue timedValue = map.get(key); if (timedValue != null) { return timedValue.getValue(); } else if (isCacheLoaderExist()) { final V value = cacheLoader.apply(key); if (value != null) { put(key, value); } return value; } else { return null; } } @Override public V getOrLoad(final K key, final Function loader) { Preconditions.checkNotNull(loader, "loader parameter in #getOrLoad() is null"); final V value = get(key); return value == null ? loader.apply(key) : value; } @Override public void put(final K key, final V value) { Preconditions.checkNotNull(key, "key in #put() is null"); Preconditions.checkNotNull(value, "value in #put() is null"); map.put(key, new TimedValue<>(timeoutMillis, value)); } @Override public void invalidate(final K key) { Preconditions.checkNotNull(key, "Cannot invalidate. Cache with null key is not allowed"); map.remove(key); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy