Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2021 The RoboZonky Project
*
* 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 com.github.robozonky.internal.async;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.github.robozonky.internal.util.functional.Either;
/**
* Builds an instance of {@link Reloadable}.
*
* @param The type that the {@link Reloadable} should hold.
*/
public final class ReloadableBuilder {
private static final Consumer> NOOP_CONSUMER = x -> {
// do nothing
};
private static final Logger LOGGER = LogManager.getLogger(ReloadableBuilder.class);
private final Supplier supplier;
private UnaryOperator reloader;
private Function reloadAfter;
private Consumer finisher;
private final Collection> listeners = new ArrayList<>(0);
private boolean async = false;
ReloadableBuilder(final Supplier supplier) {
this.supplier = supplier;
}
/**
* When {@link Reloadable#get()} is called after this time passed since the remote operation was executed last,
* the operation will be executed again. If the operation throws an exception, the reload will fail and there would
* be no value. If not specified, the {@link Reloadable} will never reload, unless {@link Reloadable#clear()} is
* called.
*
* @param duration Duration after which to reload.
* @return This.
*/
public ReloadableBuilder reloadAfter(final Duration duration) {
return reloadAfter(x -> duration);
}
/**
* The same semantics as {@link #reloadAfter(Function)}, only the actual duration would be inferred from the value
* of the {@link Reloadable} every time its new value is fetched.
*
* @param durationFunction Function returning the duration after which to reload.
* @return This.
*/
public ReloadableBuilder reloadAfter(final Function durationFunction) {
this.reloadAfter = durationFunction;
return this;
}
/**
* While the first instance will be loaded using the supplier provided in {@link #ReloadableBuilder(Supplier)},
* every other instance will be retrieved using the function provided here.
*
* @param reloader Previous instance retrieved by previous invocation of the supplier above or the function
* provided here.
* @return This.
*/
public ReloadableBuilder reloadWith(final UnaryOperator reloader) {
this.reloader = reloader;
return this;
}
/**
* Add a listener to run every time a value changes.
*
* @param changeListener The instance to listen for changes.
* @return This.
*/
public ReloadableBuilder addListener(final ReloadListener changeListener) {
listeners.add(changeListener);
return this;
}
/**
* When the operation is successfully executed without errors, the given finisher has to be executed as well. If it
* throws an exception, the operation itself is considered failed.
*
* @param consumer The finisher instance.
* @return This.
*/
public ReloadableBuilder finishWith(final Consumer consumer) {
this.finisher = consumer;
return this;
}
/**
* If specified, {@link Reloadable#get()} will only trigger the operation on the background and return the
* (now stale) value stored previously. If the background operation fails, the stale value will continue to be
* returned. On the first {@link Reloadable#get()} call, the operation will be performed synchronously, as there
* would otherwise be no stale value to return.
*
* @return This.
*/
public ReloadableBuilder async() {
this.async = true;
return this;
}
/**
* Build an initialized instance. Will call {@link #build()}, immediately following it up with a call to
* {@link Reloadable#get()}.
*
* @return New initialized instance.
*/
public Either> buildEager() {
final Reloadable result = build();
LOGGER.debug("Running before returning: {}.", result);
final Either executed = result.get();
return executed.mapRight(r -> result);
}
/**
* Build an empty instance. It will be initialized, executing the remote operation, whenever
* {@link Reloadable#get()} is first called.
*
* @return New instance.
*/
public Reloadable build() {
final Consumer finish = finisher == null ? (Consumer) NOOP_CONSUMER : finisher;
final UnaryOperator reload = reloader == null ? t -> supplier.get() : reloader;
if (reloadAfter == null) {
return async ? new AsyncReloadableImpl<>(supplier, reload, finish, listeners)
: new ReloadableImpl<>(supplier, reload, finish, listeners);
} else {
return async ? new AsyncReloadableImpl<>(supplier, reload, finish, listeners, reloadAfter)
: new ReloadableImpl<>(supplier, reload, finish, listeners, reloadAfter);
}
}
}