Home / Class/ SoftReferenceConfigurationPropertyCache Class — spring-boot Architecture

SoftReferenceConfigurationPropertyCache Class — spring-boot Architecture

Architecture documentation for the SoftReferenceConfigurationPropertyCache class in SoftReferenceConfigurationPropertyCache.java from the spring-boot codebase.

Entity Profile

Source Code

core/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SoftReferenceConfigurationPropertyCache.java lines 35–158

class SoftReferenceConfigurationPropertyCache<T> implements ConfigurationPropertyCaching {

	private static final Duration UNLIMITED = Duration.ZERO;

	static final CacheOverride NO_OP_OVERRIDE = () -> {
	};

	private final boolean neverExpire;

	private volatile @Nullable Duration timeToLive;

	private volatile SoftReference<@Nullable T> value = new SoftReference<>(null);

	private volatile @Nullable Instant lastAccessed = now();

	SoftReferenceConfigurationPropertyCache(boolean neverExpire) {
		this.neverExpire = neverExpire;
	}

	@Override
	public void enable() {
		this.timeToLive = UNLIMITED;
	}

	@Override
	public void disable() {
		this.timeToLive = null;
	}

	@Override
	public void setTimeToLive(@Nullable Duration timeToLive) {
		this.timeToLive = (timeToLive == null || timeToLive.isZero()) ? null : timeToLive;
	}

	@Override
	public void clear() {
		this.lastAccessed = null;
	}

	@Override
	public CacheOverride override() {
		if (this.neverExpire) {
			return NO_OP_OVERRIDE;
		}
		ActiveCacheOverride override = new ActiveCacheOverride(this);
		if (override.timeToLive() == null) {
			// Ensure we don't use stale data on the first access
			clear();
		}
		this.timeToLive = UNLIMITED;
		return override;
	}

	void restore(ActiveCacheOverride override) {
		this.timeToLive = override.timeToLive();
		this.lastAccessed = override.lastAccessed();
	}

	/**
	 * Get a value from the cache, creating it if necessary.
	 * @param factory a factory used to create the item if there is no reference to it.
	 * @param refreshAction action called to refresh the value if it has expired
	 * @return the value from the cache
	 */
	T get(Supplier<T> factory, UnaryOperator<T> refreshAction) {
		T value = getValue();
		if (value == null) {
			value = refreshAction.apply(factory.get());
			setValue(value);
		}
		else if (hasExpired()) {
			value = refreshAction.apply(value);
			setValue(value);
		}
		if (!this.neverExpire) {
			this.lastAccessed = now();
		}
		return value;
	}

	private boolean hasExpired() {
		if (this.neverExpire) {
			return false;
		}
		Duration timeToLive = this.timeToLive;
		Instant lastAccessed = this.lastAccessed;
		if (timeToLive == null || lastAccessed == null) {
			return true;
		}
		return !UNLIMITED.equals(timeToLive) && now().isAfter(lastAccessed.plus(timeToLive));
	}

	protected Instant now() {
		return Instant.now();
	}

	protected @Nullable T getValue() {
		return this.value.get();
	}

	protected void setValue(T value) {
		this.value = new SoftReference<>(value);
	}

	/**
	 * An active {@link CacheOverride} with a stored time-to-live.
	 */
	private record ActiveCacheOverride(SoftReferenceConfigurationPropertyCache<?> cache, @Nullable Duration timeToLive,
			@Nullable Instant lastAccessed, AtomicBoolean active) implements CacheOverride {

		ActiveCacheOverride(SoftReferenceConfigurationPropertyCache<?> cache) {
			this(cache, cache.timeToLive, cache.lastAccessed, new AtomicBoolean());
		}

		@Override
		public void close() {
			if (active().compareAndSet(false, true)) {
				this.cache.restore(this);
			}
		}

	}

}

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free