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 2015 Peter Nerg
*
* 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 javascalautils.concurrent;
import static javascalautils.Option.None;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import javascalautils.Failure;
import javascalautils.Option;
import javascalautils.Success;
import javascalautils.Try;
import javascalautils.Validator;
/**
* The future implementation.
*
* @author Peter Nerg
* @since 1.2
*/
final class FutureImpl implements Future {
/**
* Contains either a {@link Success} as a result of {@link #success(Object)} or a {@link Failure} as a result of {@link #failure(Throwable)}
*/
private Option> response = None();
/** The success handlers set by the user. */
private final List> successHandlers = new ArrayList<>();
/** The failure handlers set by the user. */
private final List> failureHandlers = new ArrayList<>();
/** The complete handlers set by the user. */
private final List>> completeHandlers = new ArrayList<>();
@Override
public boolean isCompleted() {
return response.isDefined();
}
@Override
public Option> value() {
return response;
}
@Override
public void onFailure(Consumer c) {
Validator.requireNonNull(c, "Null is not a valid consumer");
failureHandlers.add(new EventHandler<>(c));
response.filter(Try::isFailure).map(Try::failed).map(Try::orNull).forEach(t -> notifyHandlers(failureHandlers, t));
}
@Override
public void onSuccess(Consumer c) {
Validator.requireNonNull(c, "Null is not a valid consumer");
successHandlers.add(new EventHandler<>(c));
response.filter(Try::isSuccess).map(Try::orNull).forEach(r -> notifyHandlers(successHandlers, r));
}
/*
* (non-Javadoc)
*
* @see javascalautils.concurrent.Future#onComplete(java.util.function.Consumer)
*/
@Override
public void onComplete(Consumer> c) {
Validator.requireNonNull(c, "Null is not a valid consumer");
completeHandlers.add(new EventHandler<>(c));
response.forEach(t -> notifyHandlers(completeHandlers, t));
}
/*
* (non-Javadoc)
*
* @see javascalautils.concurrent.Future#forEach(java.util.function.Consumer)
*/
@Override
public void forEach(Consumer c) {
Validator.requireNonNull(c, "Null is not a valid consumer");
onSuccess(c);
}
/*
* (non-Javadoc)
*
* @see javascalautils.concurrent.Future#map(java.util.function.Function)
*/
@Override
public Future map(Function function) {
// the onFailure function just passed the error as-is without transformation
return transform(function, t -> t);
}
/*
* (non-Javadoc)
*
* @see javascalautils.concurrent.Future#flatMap(java.util.function.Function)
*/
@Override
public Future flatMap(Function> function) {
Validator.requireNonNull(function, "Null is not a valid function");
// Create new future expected to hold the value of the mapped type
FutureImpl future = new FutureImpl<>();
// install success handler that will map the result before applying it
onSuccess(value -> {
// use the provided function to create a mapped future
Future mapped = function.apply(value);
// install success/failure handlers on the mapped future to bridge between this instance
// and the one created a few lines above
mapped.onSuccess(v -> future.success(v));
mapped.onFailure(t -> future.failure(t));
});
// install failure handler that just passes the result through
onFailure(t -> future.failure(t));
return future;
}
/*
* (non-Javadoc)
*
* @see javascalautils.concurrent.Future#filter(java.util.function.Predicate)
*/
@Override
public Future filter(Predicate predicate) {
Validator.requireNonNull(predicate, "Null is not a valid predicate");
// Create new future expected to hold the value of the mapped type
FutureImpl future = new FutureImpl<>();
// install success handler that will filter the result before applying it
onSuccess(value -> {
if (predicate.test(value)) {
future.success(value);
} else {
future.failure(new NoSuchElementException("The predicate failed on value [" + value + "]"));
}
});
// install failure handler that just passes the result through
onFailure(t -> future.failure(t));
return future;
}
/*
* (non-Javadoc)
*
* @see javascalautils.concurrent.Future#transform(java.util.function.Function, java.util.function.Function)
*/
@Override
public Future transform(Function onSuccess, Function onFailure) {
Validator.requireNonNull(onSuccess, "Null is not a valid function");
Validator.requireNonNull(onFailure, "Null is not a valid function");
// Create new future expected to hold the value of the mapped type
FutureImpl future = new FutureImpl<>();
// install success handler that will map the result before applying it
onSuccess(value -> future.success(onSuccess.apply(value)));
// install failure handler that will map the error before applying it
onFailure(t -> future.failure(onFailure.apply(t)));
return future;
}
/*
* (non-Javadoc)
*
* @see javascalautils.concurrent.Future#recover(java.util.function.Function)
*/
@Override
public Future recover(Function recoverFunction) {
Validator.requireNonNull(recoverFunction, "Null is not a valid function");
// Create new future expected to hold the value of the mapped type
FutureImpl future = new FutureImpl<>();
// install success handler that will pass the value as-is
onSuccess(value -> future.success(value));
// install failure handler that will map the error to a value passed to the success function
onFailure(t -> future.success(recoverFunction.apply(t)));
return future;
}
/*
* (non-Javadoc)
*
* @see javascalautils.concurrent.Future#result(long, java.util.concurrent.TimeUnit)
*/
@Override
public T result(long duration, TimeUnit timeUnit) throws Throwable, TimeoutException {
Validator.requireNonNull(timeUnit, "Null is not a valid time unit");
CountDownLatch latch = new CountDownLatch(1);
// install a handler that releases the count down latch when notified with a result.
// the actual result/response is of no interest as we anyways have access to it internally in this class/instance.
onComplete(t -> latch.countDown());
// block for either the time to pass or the Future gets completed
if (!latch.await(duration, timeUnit)) {
throw new TimeoutException("Timeout waiting for Future to complete");
}
// The response is now set to Some
// return the value of the response (Try), should it be a Failure the exception is raised
return response.get().get();
}
/**
* Returns a String representation of the instance.
*/
@Override
public String toString() {
return "Future:" + response;
}
/**
* Completes this Future with a result.
* Invoked by the Promise owning this instance.
*
* @param result
*/
void complete(Try result) {
this.response = Option.apply(result);
if (result.isSuccess()) {
// the orNull is just to skip the exception handling otherwise forced by get()
notifyHandlers(successHandlers, result.orNull());
} else {
// the orNull is just to skip the exception handling otherwise forced by get()
notifyHandlers(failureHandlers, result.failed().orNull());
}
notifyHandlers(completeHandlers, result);
}
/**
* Internal report success to this future.
*
* @param value
* The response value
*/
private void success(T value) {
complete(new Success<>(value));
}
/**
* Internal report failure to this future.
*
* @param throwable
* The failure Throwable
*/
private void failure(Throwable throwable) {
complete(new Failure<>(throwable));
}
/**
* Invoke all provided handlers with the provided value.
* A filter is applied to make sure we only notify handlers that have not been notified before.
*
* @param handlers
* The handlers to notify.
* @param value
*/
private void notifyHandlers(List> handlers, R value) {
// The filter is to make sure we only respond/notify once
handlers.stream().filter(h -> !h.notified()).forEach(h -> h.notify(value));
}
/**
* Internal holder for the success/failure/complete handlers provided by the user.
* Used primarily to keep track on if a particular handler already has been notified.
* This is to make sure the same handler won't be notified more than once.
*
* @author Peter Nerg
*
* @param
*/
private static final class EventHandler {
private final Consumer consumer;
private boolean notified = false;
private EventHandler(Consumer consumer) {
this.consumer = consumer;
}
/**
* If this event handler already has been notified.
*
* @return
*/
private boolean notified() {
return notified;
}
/**
* Notifies the response to the handler.
*
* @param response
*/
private void notify(R response) {
notified = true;
consumer.accept(response);
}
}
}