package org.cache2k;
/*
* #%L
* cache2k API
* %%
* Copyright (C) 2000 - 2021 headissue GmbH, Munich
* %%
* 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.
* #L%
*/
import org.cache2k.io.AsyncBulkCacheLoader;
import org.cache2k.io.BulkCacheLoader;
import org.cache2k.operation.Scheduler;
import org.cache2k.operation.TimeReference;
import org.cache2k.annotation.Nullable;
import org.cache2k.config.Cache2kConfig;
import org.cache2k.config.CacheType;
import org.cache2k.config.ConfigBean;
import org.cache2k.config.ConfigBuilder;
import org.cache2k.config.WithSection;
import org.cache2k.config.SectionBuilder;
import org.cache2k.config.CustomizationReferenceSupplier;
import org.cache2k.config.CustomizationSupplier;
import org.cache2k.config.ConfigSection;
import org.cache2k.config.ToggleFeature;
import org.cache2k.event.CacheClosedListener;
import org.cache2k.expiry.ExpiryPolicy;
import org.cache2k.event.CacheEntryOperationListener;
import org.cache2k.io.AdvancedCacheLoader;
import org.cache2k.io.AsyncCacheLoader;
import org.cache2k.io.CacheLoader;
import org.cache2k.io.CacheWriter;
import org.cache2k.io.ExceptionPropagator;
import org.cache2k.io.ResiliencePolicy;
import org.cache2k.io.CacheLoaderException;
import org.cache2k.operation.Weigher;
import org.cache2k.processor.MutableCacheEntry;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.Duration;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Builder to create a {@link Cache} instance. The usage is:
*
* {@code
* Cache> c =
* new Cache2kBuilder>() {}
* .name("myCache")
* .eternal(true)
* .build();
* }
*
* Caches belong to a cache manager. If no cache manager is set explicitly via
* {@link #manager} the default cache manager will be used, as defined by
* {@link CacheManager#getInstance()}.
*
*
To create a cache from a known configuration in a specified cache manager, use:
*
*
{@code
* CacheManager manager = ...
* CacheConfiguration> config = ...
*
* Cache> c =
* Cache2kBuilder.of(config)
* .manager(manager)
* .build();
* }
*
* To create a cache without type parameters or {@code Cache}, use
* {@link Cache2kBuilder#forUnknownTypes()}.
*
* This builder can also be used to alter and build a configuration object that
* can be retrieved with {@link #config()}
*
* @author Jens Wilke
* @since 1.0
*/
@SuppressWarnings("nullness")
public class Cache2kBuilder
implements ConfigBuilder, Cache2kConfig>, DataAware {
private static final String MSG_NO_TYPES =
"Use Cache2kBuilder.forUnknownTypes(), to construct a builder with no key and value types";
/**
* Create a new cache builder for a cache that has no type restrictions
* or to set the type information later via the builder methods {@link #keyType} or
* {@link #valueType}.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static Cache2kBuilder forUnknownTypes() {
return new Cache2kBuilder(null, null);
}
/**
* Create a new cache builder for key and value types of classes with no generic parameters.
*
* @see #keyType(Class)
* @see #valueType(Class)
*/
public static Cache2kBuilder of(Class keyType, Class valueType) {
return new Cache2kBuilder(CacheType.of(keyType), CacheType.of(valueType));
}
/**
* Create a builder from the configuration bean. The builder is not assigned
* to a cache manager and any default configurations, if present, will not be
* applied.
*/
public static Cache2kBuilder of(Cache2kConfig c) {
Cache2kBuilder cb = new Cache2kBuilder(c);
return cb;
}
private @Nullable CacheType keyType;
private @Nullable CacheType valueType;
private @Nullable Cache2kConfig config = null;
private @Nullable CacheManager manager = null;
private Cache2kBuilder(Cache2kConfig cfg) {
withConfig(cfg);
}
/**
* Constructor to override for the usage pattern:
*
* {@code
* Cache> c =
* new Cache2kBuilder>() {}
* .name("myCache")
* .eternal(true)
* .build();
* }
*
* The builder extracts the generic type parameters from the anonymous subclass.
*/
@SuppressWarnings("unchecked")
protected Cache2kBuilder() {
Type t = this.getClass().getGenericSuperclass();
if (!(t instanceof ParameterizedType)) {
throw new IllegalArgumentException(MSG_NO_TYPES);
}
Type[] types = ((ParameterizedType) t).getActualTypeArguments();
keyType = (CacheType) CacheType.of(types[0]);
valueType = (CacheType) CacheType.of(types[1]);
if (Object.class.equals(keyType.getType()) &&
Object.class.equals(valueType.getType())) {
throw new IllegalArgumentException(MSG_NO_TYPES);
}
}
private Cache2kBuilder(@Nullable CacheType keyType, @Nullable CacheType valueType) {
this.keyType = keyType;
this.valueType = valueType;
}
private void withConfig(Cache2kConfig cfg) {
config = cfg;
}
/**
* Bind to default manager if not set before. Read in default configuration.
*/
@SuppressWarnings("unchecked")
private Cache2kConfig cfg() {
if (config == null) {
config = CacheManager.PROVIDER.getDefaultConfig(getManager());
if (keyType != null) {
config.setKeyType(keyType);
}
if (valueType != null) {
config.setValueType(valueType);
}
}
return config;
}
/**
* The manager, the created cache will belong to. If this is set, it must be the
* first method called.
*
* @param manager The manager the created cache should belong to,
* or {@code null} if the default cache manager should be used
* @throws IllegalStateException if the manager is not provided immediately after the builder
* is created.
*/
public final Cache2kBuilder manager(CacheManager manager) {
if (this.manager != null) {
throw new IllegalStateException("manager() must be first operation on builder.");
}
this.manager = manager;
return this;
}
/**
* The used type of the cache key. A suitable cache key must provide a useful
* {@code equals} and {@code hashCode} method. Arrays are not valid for cache keys.
*
* @throws IllegalArgumentException in case the type is illegal
* @see CacheType for a general discussion on types
*/
@SuppressWarnings("unchecked")
public final Cache2kBuilder keyType(Class t) {
Cache2kBuilder me = (Cache2kBuilder) this;
me.cfg().setKeyType(CacheType.of(t));
return me;
}
/**
* Sets the value type to use. Arrays are not supported.
*
* @throws IllegalArgumentException in case the type is illegal
* @see CacheType for a general discussion on types
*/
@SuppressWarnings("unchecked")
public final Cache2kBuilder valueType(Class t) {
Cache2kBuilder me = (Cache2kBuilder) this;
me.cfg().setValueType(CacheType.of(t));
return me;
}
/**
* The used type of the cache key. A suitable cache key must provide a useful
* {@code equals} and {@code hashCode} method. Arrays are not valid for cache keys.
*
* @throws IllegalArgumentException in case the type is illegal
* @see CacheType for a general discussion on types
*/
@SuppressWarnings("unchecked")
public final Cache2kBuilder keyType(CacheType t) {
Cache2kBuilder me = (Cache2kBuilder) this;
me.cfg().setKeyType(t);
return me;
}
/**
* Sets the value type to use. Arrays are not supported.
*
* @throws IllegalArgumentException in case the type is illegal
* @see CacheType for a general discussion on types
*/
@SuppressWarnings("unchecked")
public final Cache2kBuilder valueType(CacheType t) {
Cache2kBuilder me = (Cache2kBuilder) this;
me.cfg().setValueType(t);
return me;
}
/**
* Constructs a cache name out of the class name, a field name and a unique name identifying the
* component in the application. Result example:
* {@code webImagePool~com.example.ImagePool.id2Image}
*
* See {@link #name(String)} for a general discussion about cache names.
*
* @param uniqueName unique name differentiating multiple components of the same type.
* May be {@code null}.
* @see #name(String)
*/
public final Cache2kBuilder name(String uniqueName, Class> clazz, String fieldName) {
if (fieldName == null) {
throw new NullPointerException();
}
if (uniqueName == null) {
return name(clazz, fieldName);
}
cfg().setName(uniqueName + '~' + clazz.getName() + "." + fieldName);
return this;
}
/**
* Constructs a cache name out of the class name and field name. Result example:
* {@code com.example.ImagePool.id2Image}
*
* See {@link #name(String)} for a general discussion about cache names.
*
* @see #name(String)
*/
public final Cache2kBuilder name(Class> clazz, String fieldName) {
if (fieldName == null) {
throw new NullPointerException();
}
cfg().setName(clazz.getName() + "." + fieldName);
return this;
}
/**
* Sets a cache name from the fully qualified class name.
*
* See {@link #name(String)} for a general discussion about cache names.
*
* @see #name(String)
*/
public final Cache2kBuilder name(Class> clazz) {
cfg().setName(clazz.getName());
return this;
}
/**
* Sets the name of a cache. If a name is specified it must be ensured it is unique within
* the cache manager. Cache names are used at several places to have a unique ID of a cache.
* For example, for referencing additional configuration or to register JMX beans.
*
* If a name is not specified the cache generates a name automatically. The name is
* inferred from the call stack trace and contains the simple class name, the method and
* the line number of the of the caller to {@code build()}. The name also contains
* a random number. Automatically generated names don't allow reliable management, logging and
* additional configuration of caches. If no name is set, {@link #build()} will always create
* a new cache with a new unique name within the cache manager. Automatically generated
* cache names start with the character {@code '_'} as prefix to separate the names from the
* usual class name space.
*
*
For maximum compatibility cache names should only be composed with the characters
* {@code [-_.a-zA-Z0-9]}. The characters {@code {}|\^&=";:<>*?/} are not allowed in a cache name.
* The reason for restricting the characters in names, is that the names may be used to derive
* other resource names from it, e.g. for file based storage. The characters {@code *} and
* {@code ?} are used for wildcards in JMX and cannot be used in an object name.
*
*
The method is overloaded with variants to provide a naming convention of names.
*
*
For brevity within log messages and other displays the cache name may be
* shortened if the manager name is included as prefix.
*
* @see Cache#getName()
*/
public final Cache2kBuilder name(String v) {
cfg().setName(v);
return this;
}
/**
* Expired data is kept in the cache until the entry is evicted. This consumes memory,
* but if the data is accessed again the previous data can be used by the cache loader
* for optimizing (e.g. if-modified-since for a HTTP request). Default value: {@code false}
*
* @see AdvancedCacheLoader
*/
public final Cache2kBuilder keepDataAfterExpired(boolean v) {
cfg().setKeepDataAfterExpired(v);
return this;
}
/**
* The maximum number of entries hold by the cache. When the maximum size is reached, by
* inserting new entries, the cache eviction algorithm will remove one or more entries
* to keep the size within the configured limit.
*
* The value {@code Long.MAX_VALUE} means the capacity is not limited.
*
*
The default value is {@link Cache2kConfig#DEFAULT_ENTRY_CAPACITY}. The odd value
* is intentional, to serve as an indicate for SREs that the cache is running on its default
* size. A low capacity default is different to caches like Guava or Caffeine, that create
* an unbounded cache by default, which potentially a memory leak.
*/
public final Cache2kBuilder entryCapacity(long v) {
cfg().setEntryCapacity(v);
return this;
}
/**
* When set to {@code true}, cached values do not expire by time. Entries will need to be removed
* from the cache explicitly or will be evicted if capacity constraints are reached.
*
* Setting eternal to {@code false} signals that the data should expire, but there is no
* predefined expiry value at programmatic level. This value needs to be set by other
* means, e.g. within a configuration file.
*
*
The default behavior of the cache is identical to the setting of eternal. Entries will
* not expire. When eternal was set explicitly it cannot be reset to another value afterwards.
* This should protect against misconfiguration.
*
* @throws IllegalArgumentException in case a previous setting is reset
*/
public final Cache2kBuilder eternal(boolean v) {
cfg().setEternal(v);
return this;
}
/**
* Time duration after insert or updated an cache entry expires.
* To switch off time based expiry use {@link #eternal(boolean)}. The expiry
* happens via a timer event and may lag approximately one second by default, see
* {@link #timerLag(long, TimeUnit)}. For exact expiry specify an
* {@link ExpiryPolicy} and enable {@link #sharpExpiry(boolean)}.
*
* If an {@link ExpiryPolicy} is specified in combination to this value,
* the maximum expiry duration is capped to the value specified here.
*
*
A value of {@code 0} or {@link org.cache2k.expiry.ExpiryTimeValues#NOW} means
* every entry should expire immediately. This can be useful to disable caching via
* configuration.
*
*
A value of {@code Long.MAX_VALUE} milliseconds or more is treated as
* eternal expiry.
*
* @throws IllegalArgumentException if {@link #eternal(boolean)} was set to true
* @see
* cache2k user guide - Expiry and Refresh
*/
public final Cache2kBuilder expireAfterWrite(long v, TimeUnit u) {
cfg().setExpireAfterWrite(toDuration(v, u));
return this;
}
/**
* Change the maximum lag time for timer events. Timer events are used for
* expiry and refresh operations. The default is approximately one second.
*/
public final Cache2kBuilder timerLag(long v, TimeUnit u) {
cfg().setTimerLag(toDuration(v, u));
return this;
}
private static Duration toDuration(long v, TimeUnit u) {
return Duration.ofMillis(u.toMillis(v));
}
/**
* Sets customization for propagating loader exceptions. By default loader exceptions
* are wrapped into a {@link CacheLoaderException}.
*/
public final Cache2kBuilder exceptionPropagator(
ExceptionPropagator super K, ? super V> ep) {
cfg().setExceptionPropagator(wrapCustomizationInstance(ep));
return this;
}
/** Wraps to factory but passes on nulls. */
private static CustomizationReferenceSupplier wrapCustomizationInstance(T obj) {
return new CustomizationReferenceSupplier(obj);
}
/**
* Enables read through operation and sets a cache loader. Different loader types
* are available: {@link CacheLoader}, {@link AdvancedCacheLoader}.
*
* @see CacheLoader for general discussion on cache loaders
*/
public final Cache2kBuilder loader(CacheLoader l) {
cfg().setLoader(wrapCustomizationInstance(l));
return this;
}
/**
* Enables read through operation and sets a cache loader. Different loader types
* are available: {@link CacheLoader}, {@link AdvancedCacheLoader}.
*
* @see CacheLoader for general discussion on cache loaders
*/
public final Cache2kBuilder loader(AdvancedCacheLoader l) {
cfg().setAdvancedLoader(wrapCustomizationInstance(l));
return this;
}
/**
* Enables read through operation and sets a cache loader. Different loader types
* are available: {@link CacheLoader}, {@link AdvancedCacheLoader},
* {@link AsyncCacheLoader}
*
* @see CacheLoader for general discussion on cache loaders
*/
public final Cache2kBuilder loader(AsyncCacheLoader l) {
cfg().setAsyncLoader(wrapCustomizationInstance(l));
return this;
}
public final Cache2kBuilder bulkLoader(AsyncBulkCacheLoader l) {
loader(l);
return this;
}
public final Cache2kBuilder bulkLoader(BulkCacheLoader l) {
loader(l);
return this;
}
/**
* Enables write through operation and sets a writer customization that gets
* called synchronously upon cache mutations. By default write through is not enabled.
*/
public final Cache2kBuilder writer(CacheWriter w) {
cfg().setWriter(wrapCustomizationInstance(w));
return this;
}
/**
* Listener that is called after a cache is closed. This is mainly used for the JCache
* integration.
*/
public final Cache2kBuilder addCacheClosedListener(CacheClosedListener listener) {
cfg().getLifecycleListeners().add(wrapCustomizationInstance(listener));
return this;
}
/**
* Add a listener. The listeners will be executed in a synchronous mode, meaning,
* further processing for an entry will stall until a registered listener is executed.
* The expiry will be always executed asynchronously.
*
* @throws IllegalArgumentException if an identical listener is already added.
* @param listener The listener to add
*/
public final Cache2kBuilder addListener(CacheEntryOperationListener listener) {
cfg().getListeners().add(wrapCustomizationInstance(listener));
return this;
}
/**
* A set of listeners. Listeners added in this collection will be
* executed in a asynchronous mode.
*
* @throws IllegalArgumentException if an identical listener is already added.
* @param listener The listener to add
*/
public final Cache2kBuilder addAsyncListener(CacheEntryOperationListener listener) {
cfg().getAsyncListeners().add(wrapCustomizationInstance(listener));
return this;
}
/**
* Set expiry policy to use.
*
* If this is specified the maximum expiry time is still limited to the value in
* {@link #expireAfterWrite}. If {@link #expireAfterWrite(long, java.util.concurrent.TimeUnit)}
* is set to 0 then expiry calculation is not used, all entries expire immediately.
*/
public final Cache2kBuilder expiryPolicy(ExpiryPolicy super K, ? super V> c) {
cfg().setExpiryPolicy(wrapCustomizationInstance(c));
return this;
}
/**
* When {@code true}, enable background refresh / refresh ahead. After the expiry time of a
* value is reached, the loader is invoked to fetch a fresh value. The old value will be
* returned by the cache, although it is expired, and will be replaced by the new value,
* once the loader is finished. In the case there are not enough loader threads available,
* the value will expire immediately and the next {@code get()} request will trigger the load.
*
* Once refreshed, the entry is in a probation period. If it is not accessed until the next
* expiry, no refresh will be done and the entry expires regularly. This means that the
* time an entry stays within the probation period is determined by the configured expiry time
* or the {@code ExpiryPolicy}. In case an entry is not accessed any more it needs to
* reach the expiry time twice before being removed from the cache.
*
*
The number of threads used to do the refresh are configured via
* {@link #loaderThreadCount(int)}
*
*
By default, refresh ahead is not enabled.
*
* @see CacheLoader
* @see #loaderThreadCount(int)
*/
public final Cache2kBuilder refreshAhead(boolean f) {
cfg().setRefreshAhead(f);
return this;
}
/**
* By default the expiry time is not exact, which means, a value might be visible for up to
* a second longer after the requested time of expiry. The time lag depends on the system load
* and the parameter {@link #timerLag(long, TimeUnit)}
* Switching to {@code true}, means that values will not be visible when the time is reached that
* {@link ExpiryPolicy} returned. This has no effect on {@link #expireAfterWrite(long, TimeUnit)}.
*/
public final Cache2kBuilder sharpExpiry(boolean f) {
cfg().setSharpExpiry(f);
return this;
}
/**
* If no separate executor is set via {@link #loaderExecutor(Executor)} the cache will
* create a separate thread pool used exclusively by it. Defines the maximum number of threads
* this cache should use for calls to the {@link CacheLoader}. The default is one thread
* per available CPU. If threads are exhausted the executor rejects the execution. If that
* happens the cache will carry out load requests in the calling thread or, in case of a refresh,
* drop the request.
*
* If a separate executor is defined the parameter has no effect.
*
* @see #loaderExecutor(Executor)
* @see #refreshExecutor(Executor)
*/
public final Cache2kBuilder loaderThreadCount(int v) {
cfg().setLoaderThreadCount(v);
return this;
}
/**
* Ensure that the cache value is stored via direct object reference and that
* no serialization takes place. Cache clients leveraging the fact that an in heap
* cache stores object references directly should set this value.
*
* If this value is not set to true this means: The key and value objects need to have a
* defined serialization mechanism and the cache may choose to transfer off the heap.
* For cache2k version 1.0 this value has no effect. It should be
* used by application developers to future proof the applications with upcoming versions.
*/
public final Cache2kBuilder storeByReference(boolean v) {
cfg().setStoreByReference(v);
return this;
}
/**
* Sets a custom resilience policy to control the cache behavior in the presence
* of exceptions from the loader. A specified policy will be ignored if
* {@link #expireAfterWrite} is set to 0.
*/
public final Cache2kBuilder resiliencePolicy(ResiliencePolicy super K, ? super V> v) {
cfg().setResiliencePolicy(wrapCustomizationInstance(v));
return this;
}
public final Cache2kBuilder resiliencePolicy(
CustomizationSupplier extends ResiliencePolicy super K, ? super V>> v) {
cfg().setResiliencePolicy(v);
return this;
}
/**
* To increase performance cache2k optimizes the eviction and does eviction in
* greater chunks. With strict eviction, the eviction is done for one entry
* as soon as the capacity constraint is met. This is primarily used for
* testing and evaluation purposes.
*/
public final Cache2kBuilder strictEviction(boolean flag) {
cfg().setStrictEviction(flag);
return this;
}
/**
* When {@code true}, {@code null} values are allowed in the cache. In the default configuration
* {@code null} values are prohibited.
*
* See the chapter in the user guide for details on {@code null} values.
*
*
When used within Spring, {@code null} are allowed by default.
*
* @see CacheLoader#load(Object)
* @see ExpiryPolicy#calculateExpiryTime(Object, Object, long, CacheEntry)
*/
public final Cache2kBuilder permitNullValues(boolean flag) {
cfg().setPermitNullValues(flag);
return this;
}
/**
* By default statistic gathering is enabled. Switching this to {@code true} will disable all
* statistics that have significant overhead. Whether the values become visible in monitoring
* can be controlled via {@link #disableMonitoring(boolean)}
*/
public final Cache2kBuilder disableStatistics(boolean flag) {
cfg().setDisableStatistics(flag);
return this;
}
/**
* Enables that time of an refresh (means update or freshness check of a value) is
* available at {@link MutableCacheEntry#getModificationTime()}.
*
* @see MutableCacheEntry#getModificationTime()
*/
public final Cache2kBuilder recordModificationTime(boolean flag) {
cfg().setRecordModificationTime(flag);
return this;
}
/**
* When {@code true}, optimize for high core counts and applications that do lots of mutations
* in the cache. When switched on, the cache will occupy slightly more memory and eviction
* efficiency may drop slightly. This overhead is negligible for big cache sizes (100K and more).
*
* Typical interactive do not need to enable this. May improve concurrency for applications
* that utilize all cores and cache operations account for most CPU cycles.
*/
public final Cache2kBuilder boostConcurrency(boolean f) {
cfg().setBoostConcurrency(f);
return this;
}
/**
* Disables reporting of cache metrics to monitoring systems or management.
* This should be set, e.g. if a cache is created dynamically and
* intended to be short lived. All extensions for monitoring or management
* respect this parameter.
*/
public final Cache2kBuilder disableMonitoring(boolean f) {
cfg().setDisableMonitoring(f);
return this;
}
/**
* Thread pool / executor service to use for triggered load operations. If no executor is
* specified the cache will create a thread pool, if needed.
*
* The loader thread pool is used by the cache for executing load requests concurrent
* to the application, when a {@link Cache#loadAll(Iterable)} is issued. If no bulk loader
* is specified the thread pool will be used to carry out load requests in parallel. If an
* {@link AsyncCacheLoader} is used, the cache itself does not use the loader executor.
*
*
The executor should reject when not enough threads are available.
* In that case the cache will use the calling thread for the load operation.
*
* @see #loaderThreadCount(int)
*/
public final Cache2kBuilder loaderExecutor(Executor v) {
cfg().setLoaderExecutor(new CustomizationReferenceSupplier(v));
return this;
}
/**
* Thread pool / executor service to use for refresh ahead operations. If not
* specified the same refresh ahead operation will use the thread pool defined by
* {@link #loaderExecutor(Executor)} or a cache local pool is created.
*
* The executor for refresh operations may reject execution when not enough resources
* are available. If a refresh is rejected, the cache entry expires normally.
*
* @see #loaderThreadCount(int)
* @see #loaderExecutor(Executor)
*/
public final Cache2kBuilder refreshExecutor(Executor v) {
cfg().setRefreshExecutor(new CustomizationReferenceSupplier(v));
return this;
}
/**
* Executor for asynchronous operations and expiry. The {@link ForkJoinPool#commonPool()} is
* used by default.
*/
public final Cache2kBuilder executor(Executor v) {
cfg().setExecutor(new CustomizationReferenceSupplier(v));
return this;
}
/**
* Use a different scheduler to run timer tasks for.
*/
public final Cache2kBuilder scheduler(Scheduler v) {
cfg().setScheduler(new CustomizationReferenceSupplier(v));
return this;
}
/**
* Use a different time reference.
*/
public final Cache2kBuilder timeReference(TimeReference v) {
cfg().setTimeReference(new CustomizationReferenceSupplier(v));
return this;
}
/**
* Executor for asynchronous listeners. If not configured the common
* asynchronous executor is used as defined by {@link #executor(Executor)}
*
* @see #addAsyncListener(CacheEntryOperationListener)
*/
public final Cache2kBuilder asyncListenerExecutor(Executor v) {
cfg().setAsyncListenerExecutor(new CustomizationReferenceSupplier(v));
return this;
}
/**
* Set the weigher to be used to calculate the entry weight. The parameter
* {@link #maximumWeight(long)} needs to be specified as well. Using a weigher has a slightly
* performance impact on the update of existing entries. When a weigher is set the
* {@link #entryCapacity(long)} parameter is ignored.
*/
public final Cache2kBuilder weigher(Weigher v) {
cfg().setWeigher(new CustomizationReferenceSupplier>(v));
return this;
}
/**
* Specifies the maximum weight of entries the cache may contain. To obtain the entry weight a
* {@link Weigher} must be specified via {@link #weigher}.
*
* The weight of an entry does not influence which entry is evicted next, but is only used to
* constrain the capacity of the cache. The cache implementation tries the best to keep the cache
* within its maximum weight limit, but eviction may kick in before or after reaching the limit.
*
* The maximum weight setting cannot be used together with {@link #entryCapacity(long)}.
*/
public final Cache2kBuilder maximumWeight(long v) {
cfg().setMaximumWeight(v);
return this;
}
/**
* Call the consumer with this builder. This can be used to apply configuration
* fragments within the fluent configuration scheme.
*/
public final Cache2kBuilder setup(Consumer> consumer) {
consumer.accept(this);
return this;
}
/** Enable a feature */
public final Cache2kBuilder enable(Class extends ToggleFeature> feature) {
ToggleFeature.enable(this, feature);
return this;
}
public final Cache2kBuilder disable(Class extends ToggleFeature> feature) {
ToggleFeature.disable(this, feature);
return this;
}
/**
* Configure a config section. If the section is not existing it a new
* section is created. If the section is existing the existing instance
* is modified.
*
* @param configSectionClass type of the config section that is created or altered
* @param builderAction lambda that alters the config section via its builder
* @param type of the builder for the config section
* @param the type of the config section
*/
public final , CFG extends ConfigSection>
Cache2kBuilder with(Class configSectionClass, Consumer builderAction) {
CFG section =
cfg().getSections().getSection(configSectionClass);
if (section == null) {
try {
section = configSectionClass.getConstructor().newInstance();
} catch (Exception e) {
throw new Error("config bean needs working default constructor", e);
}
cfg().getSections().add(section);
}
builderAction.accept(section.builder());
return this;
}
/**
* Execute on the underlying configuration object. This can be used to
* set customization suppliers, like {@link Cache2kConfig#setExecutor(CustomizationSupplier)}
* instead of instances.
*/
public Cache2kBuilder set(Consumer> configAction) {
configAction.accept(config());
return this;
}
/**
* Execute setup code for a feature or customization and configure
* its associated configuration section via its builder.
*
* @param setupAction function modifying the configuration
* @param builderAction function for configuring the customization
* @param the builder for the customizations' configuration section
* @param the configuration section
* @param the supplier for the customization
*/
public ,
CFG extends ConfigSection,
SUP extends WithSection & CustomizationSupplier>> Cache2kBuilder setupWith(
Function, SUP> setupAction,
Consumer builderAction) {
with(setupAction.apply(this).getConfigClass(), builderAction);
return this;
}
/**
* Executes setup code for a feature or customization which has additional parameters
* and configures it via its builder.
*
* @param enabler setup function returning am associated configuration
* @param builderAction function to configure
*/
public ,
CFG extends ConfigBean>
Cache2kBuilder setup(Function, CFG> enabler,
Consumer builderAction) {
builderAction.accept(enabler.apply(this).builder());
return this;
}
/**
* Enables a toggle feature which has additional parameters and configures it via its builder.
*
* @param featureType Type of feature which has additional bean properties
* @param builderAction function to configure the feature
*/
public , T extends ToggleFeature & ConfigBean>
Cache2kBuilder enable(Class featureType, Consumer builderAction) {
T bean = ToggleFeature.enable(this, featureType);
builderAction.accept(bean.builder());
return this;
}
/**
* Enables a feature and configures its associated configuration section via its builder.
* Be aware that a section might be existing and preconfigured already, the semantics
* are identical to {@link #with(Class, Consumer)}
*/
public , T extends ToggleFeature & WithSection,
CFG extends ConfigSection> Cache2kBuilder enableWith(
Class featureType, Consumer builderAction) {
T bean = ToggleFeature.enable(this, featureType);
with(bean.getConfigClass(), builderAction);
return this;
}
/**
* Returns the configuration object this builder operates on. Changes to the configuration also
* will influence the created cache when {@link #build()} is called. The method does not
* return the effective configuration if additional external/XML configuration is present, since
* this is applied when {@code build} is called. On the other hand, the method can be used
* to inspect the effective configuration after {@code build} completed.
*
* @return configuration objects with the parameters set in the builder.
*/
@Override
public final Cache2kConfig config() {
return cfg();
}
/**
* Get the associated cache manager.
*/
public final CacheManager getManager() {
if (manager == null) {
manager = CacheManager.getInstance();
}
return manager;
}
/**
* Builds a cache with the specified configuration parameters.
*
* If XML configuration is present, the section for the cache name is looked up and
* the configuration in it applied, before the cache is build.
*
* @throws IllegalArgumentException if a cache of the same name is already active in the
* cache manager
* @throws IllegalArgumentException if a configuration entry for the named cache is required but
* not present
*/
public final Cache build() {
return CacheManager.PROVIDER.createCache(getManager(), cfg());
}
}