io.github.mike10004.containment.lifecycle.LifecyclingCachingProvider Maven / Gradle / Ivy
The newest version!
package io.github.mike10004.containment.lifecycle;
import javax.annotation.Nullable;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static java.util.Objects.requireNonNull;
/**
* Implementation of a provider of a resource that has a lifecycle.
* @param provided resource type
*/
public class LifecyclingCachingProvider implements CachingProvider {
private final Lifecycle lifecycle;
private final ConcurrentCache concurrentCache;
private final Consumer super LifecycleEvent> eventListener;
private final AtomicBoolean finishInvoked;
/**
* Constructs an instance.
* @param lifecycle the lifecycle
*/
public LifecyclingCachingProvider(Lifecycle lifecycle) {
this(lifecycle, LifecycleEvent.inactiveConsumer());
}
/**
* Constructs an instance.
* @param lifecycle lifecycle
* @param eventListener event listener
*/
public LifecyclingCachingProvider(Lifecycle lifecycle, Consumer super LifecycleEvent> eventListener) {
this.lifecycle = requireNonNull(lifecycle);
concurrentCache = new ConcurrentCache();
this.eventListener = requireNonNull(eventListener);
finishInvoked = new AtomicBoolean(false);
}
private static class LifecycleFinishedException extends RuntimeException {}
/**
* Returns a provision, after computing or recalling the cached computation result.
* @return the provision
*/
@Override
public final Provision provide() {
boolean alreadyInvoked = finishInvoked.get();
if (alreadyInvoked) {
return Provision.failed(new LifecycleFinishedException());
}
notify(LifecycleEvent.Category.PROVIDE_STARTED);
AtomicBoolean computed = new AtomicBoolean(false);
Provision invocation = concurrentCache.compute(new Supplier>(){
@Override
public Provision get() {
computed.set(true);
return computeOnce();
}
});
notify(LifecycleEvent.Category.PROVIDE_COMPLETED, String.format("%s %s", computed.get() ? "computed" : "recalled", invocation));
return invocation;
}
protected D doCommission() throws Exception {
notify(LifecycleEvent.Category.COMMISSION_STARTED);
D commissioned = lifecycle.commission();
Verify.verifyNotNull(commissioned, "lifecycle produced non-null commission() result");
return commissioned;
}
protected Provision computeOnce() {
try {
D val = doCommission();
notify(LifecycleEvent.Category.COMMISSION_SUCCEEDED);
return Computation.succeeded(val);
} catch (Throwable t) {
notify(LifecycleEvent.Category.COMMISSION_FAILED);
return Computation.failed(t);
}
}
/**
* Finishes the lifecycle of the cached object.
*/
public void finishLifecycle() {
boolean firstInvocation = finishInvoked.compareAndSet(false, true);
if (!firstInvocation) {
return;
}
notify(LifecycleEvent.of(LifecycleEvent.Category.FINISH_STARTED));
try {
lifecycle.decommission();
} catch (RuntimeException t) {
handleTearDownError(t);
}
notify(LifecycleEvent.of(LifecycleEvent.Category.FINISH_COMPLETED));
}
protected void handleTearDownError(RuntimeException t) {
throw t;
}
private class ConcurrentCache {
private final ConcurrentMap
© 2015 - 2025 Weber Informatics LLC | Privacy Policy