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

io.helidon.security.providers.common.EvictableCache Maven / Gradle / Ivy

There is a newer version: 4.1.6
Show newest version
/*
 * Copyright (c) 2018, 2022 Oracle and/or its affiliates.
 *
 * 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 io.helidon.security.providers.common;

import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;

import io.helidon.config.Config;
import io.helidon.config.metadata.Configured;
import io.helidon.config.metadata.ConfiguredOption;

/**
 * Generic cache with eviction support.
 * Default implementation is backed by {@link java.util.concurrent.ConcurrentHashMap} and provides
 * configuration to set this map up, as can be done through {@link #builder()}, and {@link #create(io.helidon.config.Config)}.
 *
 * Cache timeouts:
 * 
    *
  • {@link io.helidon.security.providers.common.EvictableCache.Builder#overallTimeout(long, java.util.concurrent.TimeUnit)} * defines the timeout of record since its creation
  • *
  • {@link io.helidon.security.providers.common.EvictableCache.Builder#timeout(long, java.util.concurrent.TimeUnit)} * defines the timeout of record since last use (a sliding timeout)
  • *
* * @param type of keys in this cache * @param type of values in this cache */ public interface EvictableCache { /** * Default timeout of records in minutes (inactivity timeout). * Default values are valid for default implementation, custom implementations * may not support such features. */ long CACHE_TIMEOUT_MINUTES = 60; /** * Default eviction period in minutes (how often to evict records). * Default values are valid for default implementation, custom implementations * may not support such features. */ long CACHE_EVICT_PERIOD_MINUTES = 5; /** * Default eviction delay in minutes (how long to wait after the cache is started). * Default values are valid for default implementation, custom implementations * may not support such features. */ long CACHE_EVICT_DELAY_MINUTES = 1; /** * Maximal number of records in the cache. * If the cache is full, no caching is done and the supplier of value is called for every uncached value. * Default values are valid for default implementation, custom implementations * may not support such features. */ long CACHE_MAX_SIZE = 100_000; /** * Parameter to {@link ConcurrentHashMap#forEachKey(long, Consumer)} used for eviction. * Default values are valid for default implementation, custom implementations * may not support such features. */ long EVICT_PARALLELISM_THRESHOLD = 10000; /** * Create a new builder for a cache that uses the default implementation. * * @param type of keys in the cache * @param type of values in the cache * @return a builder to build the cache */ static Builder builder() { return new Builder<>(); } /** * Create a new cache with default values using the default implementation. * * @param type of keys in the cache * @param type of values in the cache * @return new cache built with default values */ static EvictableCache create() { Builder builder = builder(); return builder.build(); } /** * Create a new cache and configure it from the provided configuration. * See {@link Builder#config(Config)} for the list of configuration keys. * This will use the default implementation. * * @param config config to read configuration of this cache from * @param type of keys in the cache * @param type of values in the cache * @return new cache configured from config */ static EvictableCache create(Config config) { Builder builder = builder(); return builder.config(config) .build(); } /** * Create a new cache that is not a cache (e.g. never caches, delegates * all calls to the {@link Supplier} in {@link #computeValue(Object, Supplier)}. * * @param Type of keys * @param Type of values * @return a new instance that is not caching */ @SuppressWarnings("unchecked") static EvictableCache noCache() { // there is no support for restricted visibility in interface in current java version return (EvictableCache) EvictableCacheImpl.NO_CACHE; } /** * Remove a key from the cache. Return the value if it was cached and valid. * * @param key key to remove * @return value if it was removed and valid, empty otherwise */ default Optional remove(K key) { return Optional.empty(); } /** * Get current cached value if valid. * * @param key key to use * @return current value in the cache or empty if not present (or invalid) */ default Optional get(K key) { return Optional.empty(); } /** * Current size of the cache. * * This value may not represent exact current size, as the implementation is expected to be thread safe and * accessed from multiple threads, that may change the size in parallel. * * @return current size of the cache (including valid and invalid - not yet evicted - values) */ default int size() { return 0; } /** * Either return a cached value or compute it and cache it. * * @param key key to check/insert value for * @param valueSupplier supplier called if the value is not yet cached, or is invalid * @return current value from the cache, or computed value from the supplier */ default Optional computeValue(K key, Supplier> valueSupplier) { return valueSupplier.get(); } /** * Close this cache. * Carry out shutdown tasks (e.g. shutting down eviction thread). */ default void close() { } /** * Builder to create instances of {@link EvictableCache} using the default implementation backed by * a {@link java.util.concurrent.ConcurrentHashMap}. * * @param types of keys used in the cache * @param types of values used in the cache */ @Configured class Builder implements io.helidon.common.Builder, EvictableCache> { private boolean cacheEnabled = true; private long cacheTimeout = CACHE_TIMEOUT_MINUTES; private TimeUnit cacheTimeoutUnit = TimeUnit.MINUTES; private long overallTimeout = CACHE_TIMEOUT_MINUTES; private TimeUnit overallTimeoutUnit = TimeUnit.MINUTES; private long cacheMaxSize = CACHE_MAX_SIZE; private long cacheEvictDelay = CACHE_EVICT_DELAY_MINUTES; private long cacheEvictPeriod = CACHE_EVICT_PERIOD_MINUTES; private TimeUnit cacheEvictTimeUnit = TimeUnit.MINUTES; private long parallelismThreshold = EVICT_PARALLELISM_THRESHOLD; private BiFunction evictor = (key, value) -> false; /** * Build a new instance of the cache based on configuration of this builder. * * @return a new instance of the cache */ @Override public EvictableCache build() { if (cacheEnabled) { return new EvictableCacheImpl<>(this); } else { return noCache(); } } /** * Configure record timeout since last access. * * @param timeout timeout value * @param timeoutUnit timeout unit * @return updated builder instance */ @ConfiguredOption(key = "cache-timeout-millis", value = "3600000", type = Long.class) public Builder timeout(long timeout, TimeUnit timeoutUnit) { this.cacheTimeout = timeout; this.cacheTimeoutUnit = timeoutUnit; return this; } /** * Configure record timeout since its creation. * * @param timeout timeout value * @param timeoutUnit timeout unit * @return updated builder instance */ @ConfiguredOption(key = "cache-overall-timeout-millis", value = "3600000", type = Long.class) public Builder overallTimeout(long timeout, TimeUnit timeoutUnit) { this.overallTimeout = timeout; this.overallTimeoutUnit = timeoutUnit; return this; } /** * Configure maximal cache size. * * @param cacheMaxSize maximal number of records to store in the cache * @return updated builder instance */ @ConfiguredOption("100000") public Builder maxSize(long cacheMaxSize) { this.cacheMaxSize = cacheMaxSize; return this; } /** * Configure eviction scheduling. * * @param evictDelay delay from the creation of the cache to first eviction * @param evictPeriod how often to evict records * @param evictTimeUnit time unit to use for these values * @return updated builder instance */ @ConfiguredOption(key = "cache-evict-delay-millis", value = "60000", type = Long.class, description = "Delay from the creation of the cache to first eviction") @ConfiguredOption(key = "cache-evict-period-millis", value = "300000", type = Long.class, description = "How often to evict records") public Builder evictSchedule(long evictDelay, long evictPeriod, TimeUnit evictTimeUnit) { this.cacheEvictDelay = evictDelay; this.cacheEvictPeriod = evictPeriod; this.cacheEvictTimeUnit = evictTimeUnit; return this; } /** * Configure parallelism threshold. * * @param parallelismThreshold see {@link ConcurrentHashMap#forEachKey(long, Consumer)} * @return updated builder instance */ @ConfiguredOption("10000") public Builder parallelismThreshold(long parallelismThreshold) { this.parallelismThreshold = parallelismThreshold; return this; } /** * Configure evictor to check if a record is still valid. * This should be a fast way to check, as it is happening in a {@link ConcurrentHashMap#forEachKey(long, Consumer)}. * This is also called during all get and remove operations to only return valid records. * * @param evictor evictor to use, return {@code true} for records that should be evicted, {@code false} for records * that should stay in cache * @return updated builder instance */ @ConfiguredOption(key = "evictor-class", type = Class.class) public Builder evictor(BiFunction evictor) { this.evictor = evictor; return this; } /** * If the cacheEnabled is set to false, no caching will be done. * Otherwise (default behavior) evictable caching will be used. * * @param cacheEnabled whether to enable this cache or not (true - enabled by default) * @return updated builder instance */ @ConfiguredOption("true") public Builder cacheEnabled(boolean cacheEnabled) { this.cacheEnabled = cacheEnabled; return this; } /** * Update this builder from configuration. * * Options expected under the current config node: * * * * * * * * * *
Configuration parameters
keydefault valuedescription
cache-enabledtrueSet to false to fully disable caching (and ignore other parameters) *
cache-timeout-millis{@value #CACHE_TIMEOUT_MINUTES} minutesTimeout of records in the cache * in milliseconds
cache-evict-delay-millis{@value #CACHE_EVICT_DELAY_MINUTES} minutesHow long to wait with * eviction after the cache is created in milliseconds
cache-evict-period-millis{@value #CACHE_EVICT_PERIOD_MINUTES} minutesHow often to evict * records from the cache in milliseconds
parallelism-treshold{@value #EVICT_PARALLELISM_THRESHOLD}see * {@link #parallelismThreshold(long)}
evictor-classA class that is instantiated and used as an evictor for this instance
* * @param config Config to use to load configuration options for this builder * @return updated builder instance */ public Builder config(Config config) { config.get("cache-enabled").asBoolean().ifPresent(this::cacheEnabled); if (cacheEnabled) { config.get("max-size").asInt().ifPresent(this::maxSize); config.get("cache-timeout-millis").asLong().ifPresent(timeout -> timeout(timeout, TimeUnit.MILLISECONDS)); config.get("cache-overall-timeout-millis").asLong() .ifPresent(timeout -> overallTimeout(timeout, TimeUnit.MILLISECONDS)); long evictDelay = config.get("cache-evict-delay-millis").asLong() .orElse(cacheEvictTimeUnit.toMillis(cacheEvictDelay)); long evictPeriod = config.get("cache-evict-period-millis").asLong() .orElse(cacheEvictTimeUnit.toMillis(cacheEvictPeriod)); evictSchedule(evictDelay, evictPeriod, TimeUnit.MILLISECONDS); config.get("parallelism-treshold").asLong().ifPresent(this::parallelismThreshold); config.get("evictor-class").as(Class.class).ifPresent(this::evictorClass); } return this; } @SuppressWarnings("unchecked") private Builder evictorClass(Class aClass) { // attempt to create an instance try { aClass.getMethod("apply", Object.class, Object.class); Object anObject = aClass.getConstructor().newInstance(); evictor((BiFunction) anObject); } catch (ReflectiveOperationException e) { throw new SecurityException("Failed to create an evictor instance. Configured class: " + aClass.getName(), e); } return this; } long cacheTimeout() { return cacheTimeout; } TimeUnit cacheTimeoutUnit() { return cacheTimeoutUnit; } long overallTimeout() { return overallTimeout; } TimeUnit overallTimeoutUnit() { return overallTimeoutUnit; } long cacheMaxSize() { return cacheMaxSize; } long cacheEvictDelay() { return cacheEvictDelay; } long cacheEvictPeriod() { return cacheEvictPeriod; } TimeUnit cacheEvictTimeUnit() { return cacheEvictTimeUnit; } long parallelismThreshold() { return parallelismThreshold; } BiFunction evictor() { return evictor; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy