diff --git a/README.md b/README.md index 761ee51..80ea3a6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Futur4J is a powerful and intuitive open-source Java library that simplifies asynchronous task scheduling, inspired by the concept of JavaScript promises. +**This documentation is outdated. Please don't read it.** + ## Dependency The Futur4J project is composed of multiple modules. It is required to include the `futur-api` module, and the other modules depend on it at runtime, however the others are optional and dependent on your use case. ### Gradle diff --git a/build.gradle b/build.gradle index 9d9df61..f811632 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ plugins { + id 'java' id 'com.github.johnrengelman.shadow' version '8.1.1' id 'io.github.gradle-nexus.publish-plugin' version '1.3.0' } @@ -12,8 +13,8 @@ nexusPublishing { } subprojects { - group = "dev.tommyjs" - version = "2.1.3" + group = 'dev.tommyjs' + version = '2.2.0' apply plugin: 'java' apply plugin: 'com.github.johnrengelman.shadow' @@ -27,4 +28,23 @@ subprojects { repositories { mavenCentral() } + + dependencies { + implementation 'org.jetbrains:annotations:24.1.0' + implementation 'org.slf4j:slf4j-api:2.0.12' + compileOnly 'io.projectreactor:reactor-core:3.6.4' + + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testImplementation 'io.projectreactor:reactor-core:3.6.4' + testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' + testImplementation 'org.slf4j:slf4j-api:2.0.12' + testImplementation 'ch.qos.logback:logback-classic:1.5.3' + } + + test { + useJUnitPlatform() + testLogging { + exceptionFormat = 'full' + } + } } \ No newline at end of file diff --git a/futur-api/build.gradle b/futur-api/build.gradle index 6ba9400..e69de29 100644 --- a/futur-api/build.gradle +++ b/futur-api/build.gradle @@ -1,4 +0,0 @@ -dependencies { - implementation("org.jetbrains:annotations:24.1.0") - implementation("org.slf4j:slf4j-api:2.0.12") -} \ No newline at end of file diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/DualPoolExecutor.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/DualPoolExecutor.java index bbb1dec..ff35da6 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/executor/DualPoolExecutor.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/DualPoolExecutor.java @@ -4,9 +4,10 @@ import org.jetbrains.annotations.NotNull; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -public class DualPoolExecutor implements PromiseExecutor { +public class DualPoolExecutor implements PromiseExecutor> { private final @NotNull ScheduledExecutorService syncSvc; private final @NotNull ScheduledExecutorService asyncSvc; @@ -16,18 +17,23 @@ public class DualPoolExecutor implements PromiseExecutor { this.asyncSvc = asyncSvc; } - @Override - public void runSync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { - syncSvc.schedule(task, delay, unit); - } - - @Override - public void runAsync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { - asyncSvc.schedule(task, delay, unit); - } - public static @NotNull DualPoolExecutor create(int asyncPoolSize) { return new DualPoolExecutor(Executors.newSingleThreadScheduledExecutor(), Executors.newScheduledThreadPool(asyncPoolSize)); } + @Override + public ScheduledFuture runSync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { + return syncSvc.schedule(task, delay, unit); + } + + @Override + public ScheduledFuture runAsync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { + return asyncSvc.schedule(task, delay, unit); + } + + @Override + public void cancel(ScheduledFuture task) { + task.cancel(true); + } + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseExecutor.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseExecutor.java index e1b0f9e..caeaad8 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseExecutor.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseExecutor.java @@ -4,18 +4,20 @@ import org.jetbrains.annotations.NotNull; import java.util.concurrent.TimeUnit; -public interface PromiseExecutor { +public interface PromiseExecutor { - void runSync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit); + T runSync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit); - void runAsync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit); + T runAsync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit); - default void runSync(@NotNull Runnable task) { - runSync(task, 0L, TimeUnit.MILLISECONDS); + default T runSync(@NotNull Runnable task) { + return runSync(task, 0L, TimeUnit.MILLISECONDS); } - default void runAsync(@NotNull Runnable task) { - runAsync(task, 0L, TimeUnit.MILLISECONDS); + default T runAsync(@NotNull Runnable task) { + return runAsync(task, 0L, TimeUnit.MILLISECONDS); } + void cancel(T task); + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/SinglePoolExecutor.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/SinglePoolExecutor.java index 310ad11..c63dab6 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/executor/SinglePoolExecutor.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/SinglePoolExecutor.java @@ -4,24 +4,11 @@ import org.jetbrains.annotations.NotNull; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -public class SinglePoolExecutor implements PromiseExecutor { - - private final @NotNull ScheduledExecutorService service; +public class SinglePoolExecutor extends DualPoolExecutor { + public SinglePoolExecutor(@NotNull ScheduledExecutorService service) { - this.service = service; - } - - @Override - public void runSync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { - service.schedule(task, delay, unit); - } - - @Override - public void runAsync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { - service.schedule(task, delay, unit); + super(service, service); } public static @NotNull SinglePoolExecutor create(int threadPoolSize) { diff --git a/futur-api/src/main/java/dev/tommyjs/futur/impl/SimplePromise.java b/futur-api/src/main/java/dev/tommyjs/futur/impl/SimplePromise.java index df268fe..8427c8a 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/impl/SimplePromise.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/impl/SimplePromise.java @@ -2,33 +2,25 @@ package dev.tommyjs.futur.impl; import dev.tommyjs.futur.executor.PromiseExecutor; import dev.tommyjs.futur.promise.AbstractPromise; -import dev.tommyjs.futur.promise.PromiseFactory; +import dev.tommyjs.futur.promise.AbstractPromiseFactory; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; -public class SimplePromise extends AbstractPromise { +public class SimplePromise extends AbstractPromise { - private final PromiseExecutor executor; - private final Logger logger; - private final PromiseFactory factory; + private final @NotNull AbstractPromiseFactory factory; - public SimplePromise(PromiseExecutor executor, Logger logger, PromiseFactory factory) { - this.executor = executor; - this.logger = logger; + public SimplePromise(@NotNull AbstractPromiseFactory factory) { this.factory = factory; } - @Override - protected PromiseExecutor getExecutor() { - return executor; + @Deprecated + public SimplePromise(@NotNull PromiseExecutor executor, @NotNull Logger logger, @NotNull AbstractPromiseFactory factory) { + this(factory); } @Override - protected Logger getLogger() { - return logger; - } - - @Override - public PromiseFactory getFactory() { + public @NotNull AbstractPromiseFactory getFactory() { return factory; } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/impl/SimplePromiseFactory.java b/futur-api/src/main/java/dev/tommyjs/futur/impl/SimplePromiseFactory.java index 47c8b7b..1bc6ecb 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/impl/SimplePromiseFactory.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/impl/SimplePromiseFactory.java @@ -1,39 +1,34 @@ package dev.tommyjs.futur.impl; import dev.tommyjs.futur.executor.PromiseExecutor; -import dev.tommyjs.futur.promise.AbstractPromise; +import dev.tommyjs.futur.promise.AbstractPromiseFactory; import dev.tommyjs.futur.promise.Promise; -import dev.tommyjs.futur.promise.PromiseFactory; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; -public class SimplePromiseFactory implements PromiseFactory { +public class SimplePromiseFactory extends AbstractPromiseFactory { - private final PromiseExecutor executor; + private final PromiseExecutor executor; private final Logger logger; - public SimplePromiseFactory(PromiseExecutor executor, Logger logger) { + public SimplePromiseFactory(PromiseExecutor executor, Logger logger) { this.executor = executor; this.logger = logger; } - @Override - public @NotNull Promise resolve(T value) { - AbstractPromise promise = new SimplePromise<>(executor, logger, this); - promise.complete(value); - return promise; - } - @Override public @NotNull Promise unresolved() { - return new SimplePromise<>(executor, logger, this); + return new SimplePromise<>(this); } @Override - public @NotNull Promise error(Throwable error) { - AbstractPromise promise = new SimplePromise<>(executor, logger, this); - promise.completeExceptionally(error); - return promise; + public @NotNull Logger getLogger() { + return logger; + } + + @Override + public @NotNull PromiseExecutor getExecutor() { + return executor; } } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/impl/StaticPromise.java b/futur-api/src/main/java/dev/tommyjs/futur/impl/StaticPromise.java deleted file mode 100644 index 76e2324..0000000 --- a/futur-api/src/main/java/dev/tommyjs/futur/impl/StaticPromise.java +++ /dev/null @@ -1,25 +0,0 @@ -package dev.tommyjs.futur.impl; - -import dev.tommyjs.futur.executor.PromiseExecutor; -import dev.tommyjs.futur.promise.AbstractPromise; -import dev.tommyjs.futur.promise.PromiseFactory; -import org.slf4j.Logger; - -public class StaticPromise extends AbstractPromise { - - @Override - protected PromiseExecutor getExecutor() { - return StaticPromiseFactory.EXECUTOR; - } - - @Override - protected Logger getLogger() { - return StaticPromiseFactory.LOGGER; - } - - @Override - public PromiseFactory getFactory() { - return StaticPromiseFactory.INSTANCE; - } - -} diff --git a/futur-api/src/main/java/dev/tommyjs/futur/impl/StaticPromiseFactory.java b/futur-api/src/main/java/dev/tommyjs/futur/impl/StaticPromiseFactory.java deleted file mode 100644 index a5e39ea..0000000 --- a/futur-api/src/main/java/dev/tommyjs/futur/impl/StaticPromiseFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -package dev.tommyjs.futur.impl; - -import dev.tommyjs.futur.executor.PromiseExecutor; -import dev.tommyjs.futur.executor.SinglePoolExecutor; -import dev.tommyjs.futur.promise.AbstractPromise; -import dev.tommyjs.futur.promise.Promise; -import dev.tommyjs.futur.promise.PromiseFactory; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.Executors; - -public class StaticPromiseFactory implements PromiseFactory { - - public static final @NotNull PromiseFactory INSTANCE; - public static final @NotNull PromiseExecutor EXECUTOR; - public static final @NotNull Logger LOGGER; - - static { - INSTANCE = new StaticPromiseFactory(); - EXECUTOR = SinglePoolExecutor.create(1); - LOGGER = LoggerFactory.getLogger(StaticPromiseFactory.class); - } - - private StaticPromiseFactory() { - } - - @Override - public @NotNull Promise resolve(T value) { - AbstractPromise promise = new StaticPromise<>(); - promise.complete(value); - return promise; - } - - @Override - public @NotNull Promise unresolved() { - return new StaticPromise<>(); - } - - @Override - public @NotNull Promise error(Throwable error) { - AbstractPromise promise = new StaticPromise<>(); - promise.completeExceptionally(error); - return promise; - } - -} diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromise.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromise.java index 0c68588..7cbd0d1 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromise.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromise.java @@ -10,12 +10,14 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import java.util.Collection; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; -public abstract class AbstractPromise implements Promise { +public abstract class AbstractPromise implements Promise { private final Collection> listeners; private final AtomicReference> completion; @@ -25,14 +27,14 @@ public abstract class AbstractPromise implements Promise { this.completion = new AtomicReference<>(); } - protected abstract PromiseExecutor getExecutor(); + public abstract @NotNull AbstractPromiseFactory getFactory(); - protected abstract Logger getLogger(); + protected @NotNull PromiseExecutor getExecutor() { + return getFactory().getExecutor(); + } - @Deprecated - @Override - public T join(long interval, long timeoutMillis) throws TimeoutException { - return join(timeoutMillis); + protected @NotNull Logger getLogger() { + return getFactory().getLogger(); } @Override @@ -56,7 +58,7 @@ public abstract class AbstractPromise implements Promise { } if (completion == null) - throw new TimeoutException("Promise timed out after " + timeoutMillis + "ms"); + throw new TimeoutException("Promise stopped waiting after " + timeoutMillis + "ms"); return joinCompletion(completion); } @@ -113,57 +115,47 @@ public abstract class AbstractPromise implements Promise { @Override public @NotNull Promise thenApplySync(@NotNull ExceptionalFunction task) { Promise promise = getFactory().unresolved(); - addListener(ctx -> { - if (ctx.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx.getException()); - return; - } - - Runnable runnable = createRunnable(ctx, promise, task); - getExecutor().runSync(runnable, 0L, TimeUnit.MILLISECONDS); - }); + addListener( + res -> { + Runnable runnable = createRunnable(res, promise, task); + F future = getExecutor().runSync(runnable); + promise.onCancel((e) -> getExecutor().cancel(future)); + }, + promise::completeExceptionally + ); + addChild(promise); return promise; } @Override public @NotNull Promise thenApplyDelayedSync(@NotNull ExceptionalFunction task, long delay, @NotNull TimeUnit unit) { Promise promise = getFactory().unresolved(); - addListener(ctx -> { - if (ctx.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx.getException()); - return; - } - - Runnable runnable = createRunnable(ctx, promise, task); - getExecutor().runSync(runnable, delay, unit); - }); + addListener( + res -> { + Runnable runnable = createRunnable(res, promise, task); + F future = getExecutor().runSync(runnable, delay, unit); + promise.onCancel((e) -> getExecutor().cancel(future)); + }, + promise::completeExceptionally + ); + addChild(promise); return promise; } @Override public @NotNull Promise thenComposeSync(@NotNull ExceptionalFunction> task) { Promise promise = getFactory().unresolved(); - thenApplySync(task).thenConsumeAsync(nestedPromise -> { - nestedPromise.addListener(ctx1 -> { - if (ctx1.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx1.getException()); - return; - } - - promise.complete(ctx1.getResult()); - }); - }).addListener(ctx2 -> { - if (ctx2.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx2.getException()); - } - }); + thenApplySync(task).addListener( + nestedPromise -> { + nestedPromise.propagateResult(promise); + nestedPromise.addChild(promise); + }, + promise::completeExceptionally + ); + addChild(promise); return promise; } @@ -220,83 +212,66 @@ public abstract class AbstractPromise implements Promise { @Override public @NotNull Promise thenApplyAsync(@NotNull ExceptionalFunction task) { Promise promise = getFactory().unresolved(); - addListener(ctx -> { - if (ctx.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx.getException()); - return; - } - - Runnable runnable = createRunnable(ctx, promise, task); - getExecutor().runAsync(runnable, 0L, TimeUnit.MILLISECONDS); - }); + addListener( + (res) -> { + Runnable runnable = createRunnable(res, promise, task); + F future = getExecutor().runAsync(runnable); + promise.onCancel((e) -> getExecutor().cancel(future)); + }, + promise::completeExceptionally + ); + addChild(promise); return promise; } @Override public @NotNull Promise thenApplyDelayedAsync(@NotNull ExceptionalFunction task, long delay, @NotNull TimeUnit unit) { Promise promise = getFactory().unresolved(); - addListener(ctx -> { - Runnable runnable = createRunnable(ctx, promise, task); - getExecutor().runAsync(runnable, delay, unit); - }); + addListener( + res -> { + Runnable runnable = createRunnable(res, promise, task); + F future = getExecutor().runAsync(runnable, delay, unit); + promise.onCancel((e) -> getExecutor().cancel(future)); + }, + promise::completeExceptionally + ); + addChild(promise); return promise; } @Override public @NotNull Promise thenComposeAsync(@NotNull ExceptionalFunction> task) { Promise promise = getFactory().unresolved(); - thenApplyAsync(task).thenConsumeAsync(nestedPromise -> { - nestedPromise.addListener(ctx1 -> { - if (ctx1.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx1.getException()); - return; - } - - promise.complete(ctx1.getResult()); - }); - }).addListener(ctx2 -> { - if (ctx2.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx2.getException()); - } - }); + thenApplyAsync(task).addListener( + nestedPromise -> { + nestedPromise.propagateResult(promise); + nestedPromise.addChild(promise); + }, + promise::completeExceptionally + ); + addChild(promise); return promise; } - private @NotNull Runnable createRunnable(@NotNull PromiseCompletion ctx, @NotNull Promise promise, @NotNull ExceptionalFunction task) { + private @NotNull Runnable createRunnable(T result, @NotNull Promise promise, @NotNull ExceptionalFunction task) { return () -> { - if (ctx.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx.getException()); - return; - } + if (promise.isCompleted()) return; try { - V result = task.apply(ctx.getResult()); - promise.complete(result); + V nextResult = task.apply(result); + promise.complete(nextResult); } catch (Throwable e) { promise.completeExceptionally(e); } }; } - @Override - public @NotNull Promise logExceptions() { - return logExceptions("Exception caught in promise chain"); - } - @Override public @NotNull Promise logExceptions(@NotNull String message) { - return addListener(ctx -> { - if (ctx.isError()) { - getLogger().error(message, ctx.getException()); - } - }); + return onError(e -> getLogger().error(message, e)); } @Override @@ -320,19 +295,51 @@ public abstract class AbstractPromise implements Promise { } @Override - public @NotNull Promise timeout(long time, @NotNull TimeUnit unit) { - getExecutor().runAsync(() -> { - if (!isCompleted()) { - completeExceptionally(new TimeoutException("Promise timed out after " + time + " " + unit)); + public @NotNull Promise addListener(@Nullable Consumer successListener, @Nullable Consumer errorListener) { + return addListener((res) -> { + if (res.isError()) { + if (errorListener != null) errorListener.accept(res.getException()); + } else { + if (successListener != null) successListener.accept(res.getResult()); } - }, time, unit); - - return this; + }); } @Override - public @NotNull Promise timeout(long ms) { - return timeout(ms, TimeUnit.MILLISECONDS); + public @NotNull Promise onSuccess(@NotNull Consumer listener) { + return addListener(listener, null); + } + + @Override + public @NotNull Promise onError(@NotNull Consumer listener) { + return addListener(null, listener); + } + + @Override + public @NotNull Promise onError(@NotNull Class clazz, @NotNull Consumer listener) { + return onError((e) -> { + if (clazz.isAssignableFrom(e.getClass())) { + //noinspection unchecked + listener.accept((E) e); + } + }); + } + + @Override + public @NotNull Promise onCancel(@NotNull Consumer listener) { + return onError(CancellationException.class, listener); + } + + @Deprecated + @Override + public @NotNull Promise timeout(long time, @NotNull TimeUnit unit) { + return maxWaitTime(time, unit); + } + + @Override + public @NotNull Promise maxWaitTime(long time, @NotNull TimeUnit unit) { + F future = getExecutor().runAsync(() -> completeExceptionally(new TimeoutException("Promise stopped waiting after " + time + " " + unit)), time, unit); + return onError(e -> getExecutor().cancel(future)); } private void handleCompletion(@NotNull PromiseCompletion ctx) { @@ -358,6 +365,26 @@ public abstract class AbstractPromise implements Promise { return this.completion.compareAndSet(null, completion); } + @Override + public void addChild(@NotNull Promise child) { + child.onCancel((e) -> this.cancel(e.getMessage())); + } + + @Override + public void propagateResult(@NotNull Promise target) { + addListener(target::complete, target::completeExceptionally); + } + + @Override + public void cancel() { + completeExceptionally(new CancellationException()); + } + + @Override + public void cancel(@NotNull String message) { + completeExceptionally(new CancellationException(message)); + } + @Override public void complete(@Nullable T result) { handleCompletion(new PromiseCompletion<>(result)); diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java new file mode 100644 index 0000000..607f9da --- /dev/null +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java @@ -0,0 +1,181 @@ +package dev.tommyjs.futur.promise; + +import dev.tommyjs.futur.executor.PromiseExecutor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import reactor.core.Disposable; +import reactor.core.publisher.Mono; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public abstract class AbstractPromiseFactory implements PromiseFactory { + + public abstract @NotNull PromiseExecutor getExecutor(); + + @Override + public @NotNull Promise> combine(@NotNull Promise p1, @NotNull Promise p2) { + List> promises = List.of(p1, p2); + return all(promises) + .thenApplyAsync((res) -> new AbstractMap.SimpleImmutableEntry<>( + Objects.requireNonNull(p1.getCompletion()).getResult(), + Objects.requireNonNull(p2.getCompletion()).getResult() + )); + } + + @Override + public @NotNull Promise> combine(@NotNull Map> promises, @Nullable BiConsumer exceptionHandler) { + if (promises.isEmpty()) return resolve(Collections.emptyMap()); + + Map map = new HashMap<>(); + Promise> promise = unresolved(); + for (Map.Entry> entry : promises.entrySet()) { + entry.getValue().addListener((ctx) -> { + synchronized (map) { + if (ctx.getException() != null) { + if (exceptionHandler == null) { + promise.completeExceptionally(ctx.getException()); + } else { + exceptionHandler.accept(entry.getKey(), ctx.getException()); + map.put(entry.getKey(), null); + } + } else { + map.put(entry.getKey(), ctx.getResult()); + } + + if (map.size() == promises.size()) { + promise.complete(map); + } + } + }); + } + + return promise; + } + + @Override + public @NotNull Promise> combine(@NotNull Iterable> promises, @Nullable Consumer exceptionHandler) { + AtomicInteger index = new AtomicInteger(); + return this.combine( + StreamSupport.stream(promises.spliterator(), false) + .collect(Collectors.toMap(k -> index.getAndIncrement(), v -> v)), + exceptionHandler != null ? (i, e) -> exceptionHandler.accept(e) : null + ).thenApplyAsync(v -> + v.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(Map.Entry::getValue) + .collect(Collectors.toList()) + ); + } + + @Override + public @NotNull Promise> combine(@NotNull Iterable> promises) { + return combine(promises, null); + } + + @Override + public @NotNull Promise>> allSettled(@NotNull Iterable> promiseIterable) { + List> promises = new ArrayList<>(); + promiseIterable.iterator().forEachRemaining(promises::add); + + if (promises.isEmpty()) return resolve(Collections.emptyList()); + PromiseCompletion[] results = new PromiseCompletion[promises.size()]; + + Promise>> promise = unresolved(); + var iter = promises.listIterator(); + + while (iter.hasNext()) { + int index = iter.nextIndex(); + iter.next().addListener((res) -> { + synchronized (results) { + results[index] = res; + if (Arrays.stream(results).allMatch(Objects::nonNull)) + promise.complete(Arrays.asList(results)); + } + }); + } + + return promise; + } + + @Override + public @NotNull Promise all(@NotNull Iterable> promiseIterable) { + List> promises = new ArrayList<>(); + promiseIterable.iterator().forEachRemaining(promises::add); + + if (promises.isEmpty()) return resolve(null); + AtomicInteger completed = new AtomicInteger(); + Promise promise = unresolved(); + + for (Promise p : promises) { + p.addListener((res) -> { + if (res.getException() != null) { + promise.completeExceptionally(res.getException()); + } + + if (completed.incrementAndGet() == promises.size()) { + promise.complete(null); + } + }); + } + + return promise; + } + + @Override + public @NotNull Promise wrap(@NotNull CompletableFuture future) { + Promise promise = unresolved(); + + future.whenComplete((v, e) -> { + if (e != null) { + promise.completeExceptionally(e); + } else { + promise.complete(v); + } + }); + + promise.onCancel((e) -> future.cancel(true)); + return promise; + } + + @Override + public @NotNull Promise wrap(@NotNull Mono mono) { + Promise promise = this.unresolved(); + Disposable disposable = mono.subscribe(promise::complete, promise::completeExceptionally); + promise.onCancel((e) -> disposable.dispose()); + return promise; + } + + @Override + public @NotNull Promise resolve(T value) { + Promise promise = unresolved(); + promise.complete(value); + return promise; + } + + @Override + public @NotNull Promise error(@NotNull Throwable error) { + Promise promise = unresolved(); + promise.completeExceptionally(error); + return promise; + } + + @Override + public @NotNull Promise erase(@NotNull Promise p) { + Promise promise = unresolved(); + p.addListener(ctx -> { + if (ctx.getException() != null) { + promise.completeExceptionally(ctx.getException()); + } else { + promise.complete(null); + } + }); + return promise; + } + +} diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/Promise.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/Promise.java index 75b52e4..f958f6d 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/Promise.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/Promise.java @@ -7,35 +7,16 @@ import dev.tommyjs.futur.function.ExceptionalSupplier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; public interface Promise { - static @NotNull Promise resolve(T value, PromiseFactory factory) { - return factory.resolve(value); - } - - static @NotNull Promise error(Throwable error, PromiseFactory factory) { - return factory.error(error); - } - - static @NotNull Promise unresolved(PromiseFactory factory) { - return factory.unresolved(); - } - - static @NotNull Promise start(PromiseFactory factory) { - return factory.resolve(null); - } - PromiseFactory getFactory(); - @Deprecated - T join(long interval, long timeout) throws TimeoutException; - - T join(long timeout) throws TimeoutException; - @NotNull Promise thenRunSync(@NotNull ExceptionalRunnable task); @NotNull Promise thenRunDelayedSync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit); @@ -74,22 +55,56 @@ public interface Promise { @NotNull Promise thenComposeAsync(@NotNull ExceptionalFunction> task); - @NotNull Promise logExceptions(); - @NotNull Promise logExceptions(@NotNull String message); + default @NotNull Promise logExceptions() { + return logExceptions("Exception caught in promise chain"); + } + @NotNull Promise addListener(@NotNull PromiseListener listener); + @NotNull Promise addListener(@Nullable Consumer successHandler, @Nullable Consumer errorHandler); + + @NotNull Promise onSuccess(@NotNull Consumer listener); + + @NotNull Promise onError(@NotNull Consumer listener); + + @NotNull Promise onError(@NotNull Class clazz, @NotNull Consumer listener); + + @NotNull Promise onCancel(@NotNull Consumer listener); + + @Deprecated @NotNull Promise timeout(long time, @NotNull TimeUnit unit); - @NotNull Promise timeout(long ms); + @Deprecated + default @NotNull Promise timeout(long ms) { + return timeout(ms, TimeUnit.MILLISECONDS); + } + + @NotNull Promise maxWaitTime(long time, @NotNull TimeUnit unit); + + default @NotNull Promise maxWaitTime(long ms) { + return maxWaitTime(ms, TimeUnit.MILLISECONDS); + } + + void addChild(@NotNull Promise child); + + void propagateResult(@NotNull Promise target); + + void cancel(@Nullable String reason); + + default void cancel() { + cancel(null); + } void complete(@Nullable T result); void completeExceptionally(@NotNull Throwable result); - boolean isCompleted(); + T join(long timeout) throws TimeoutException; @Nullable PromiseCompletion getCompletion(); + boolean isCompleted(); + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseCompletion.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseCompletion.java index c98411a..4ab10c9 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseCompletion.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseCompletion.java @@ -3,6 +3,8 @@ package dev.tommyjs.futur.promise; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.CancellationException; + public class PromiseCompletion { private @Nullable T result; @@ -36,6 +38,10 @@ public class PromiseCompletion { return getException() != null; } + public boolean wasCanceled() { + return getException() instanceof CancellationException; + } + public @Nullable T getResult() { return result; } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java index e46e9ff..9428abb 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java @@ -1,36 +1,61 @@ package dev.tommyjs.futur.promise; -import dev.tommyjs.futur.executor.PromiseExecutor; -import dev.tommyjs.futur.executor.SinglePoolExecutor; -import dev.tommyjs.futur.impl.SimplePromiseFactory; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; -import java.util.concurrent.Executors; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Consumer; public interface PromiseFactory { - @NotNull Promise resolve(T value); + @NotNull Logger getLogger(); @NotNull Promise unresolved(); - @NotNull Promise error(Throwable error); + @NotNull Promise> combine(@NotNull Promise p1, @NotNull Promise p2); - static PromiseFactory create(PromiseExecutor executor, Logger logger) { - return new SimplePromiseFactory(executor, logger); + @NotNull Promise> combine(@NotNull Map> promises, @Nullable BiConsumer exceptionHandler); + + default @NotNull Promise> combine(@NotNull Map> promises) { + return combine(promises, null); } - static PromiseFactory create(PromiseExecutor executor) { - return create(executor, LoggerFactory.getLogger(SimplePromiseFactory.class)); + @NotNull Promise> combine(@NotNull Iterable> promises, @Nullable Consumer exceptionHandler); + + default @NotNull Promise> combine(@NotNull Iterable> promises) { + return combine(promises, null); } - static PromiseFactory create(int threadPoolSize) { - return create(SinglePoolExecutor.create(threadPoolSize)); + @NotNull Promise>> allSettled(@NotNull Iterable> promiseIterable); + + default @NotNull Promise>> allSettled(@NotNull Promise... promiseArray) { + return allSettled(Arrays.asList(promiseArray)); } - static PromiseFactory create() { - return create(Runtime.getRuntime().availableProcessors()); + @NotNull Promise all(@NotNull Iterable> promiseIterable); + + default @NotNull Promise all(@NotNull Promise... promiseArray) { + return all(Arrays.asList(promiseArray)); } + @NotNull Promise wrap(@NotNull CompletableFuture future); + + @NotNull Promise wrap(@NotNull Mono mono); + + @NotNull Promise resolve(T value); + + default @NotNull Promise start() { + return resolve(null); + } + + @NotNull Promise error(@NotNull Throwable error); + + @NotNull Promise erase(@NotNull Promise p); + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/Promises.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/Promises.java index 02a1ad8..c598141 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/Promises.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/Promises.java @@ -4,37 +4,21 @@ import dev.tommyjs.futur.function.ExceptionalFunction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; -import java.util.stream.Collectors; +/** + * @deprecated Use PromiseFactory instance methods instead. + */ +@Deprecated public class Promises { public static @NotNull Promise> combine(@NotNull Promise p1, @NotNull Promise p2, PromiseFactory factory) { - Promise> promise = factory.unresolved(); - p1.addListener(ctx -> { - if (ctx.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx.getException()); - return; - } - - p2.addListener(ctx1 -> { - if (ctx1.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx1.getException()); - return; - } - - Map.Entry result = new AbstractMap.SimpleEntry<>(ctx.getResult(), ctx1.getResult()); - promise.complete(result); - }); - }); - - return promise; + return factory.combine(p1, p2); } public static @NotNull Promise> combine(@NotNull Promise p1, @NotNull Promise p2) { @@ -42,29 +26,7 @@ public class Promises { } public static @NotNull Promise> combine(@NotNull Map> promises, long timeout, @Nullable BiConsumer exceptionHandler, PromiseFactory factory) { - if (promises.isEmpty()) return factory.resolve(Collections.emptyMap()); - - Map map = new HashMap<>(); - Promise> promise = factory.unresolved(); - for (Map.Entry> entry : promises.entrySet()) { - entry.getValue().addListener((ctx) -> { - synchronized (map) { - if (ctx.isError()) { - if (exceptionHandler == null) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx.getException()); - } else { - exceptionHandler.accept(entry.getKey(), ctx.getException()); - map.put(entry.getKey(), null); - } - } else { - map.put(entry.getKey(), ctx.getResult()); - } - if (map.size() == promises.size()) promise.complete(map); - } - }); - } - return promise.timeout(timeout); + return factory.combine(promises, exceptionHandler).timeout(timeout); } public static @NotNull Promise> combine(@NotNull Map> promises, long timeout, boolean strict, PromiseFactory factory) { @@ -80,16 +42,7 @@ public class Promises { } public static @NotNull Promise> combine(@NotNull List> promises, long timeout, boolean strict, PromiseFactory factory) { - AtomicInteger index = new AtomicInteger(); - return combine( - promises.stream().collect(Collectors.toMap(s -> index.getAndIncrement(), v -> v)), - timeout, strict, factory - ).thenApplySync(v -> - v.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .map(Map.Entry::getValue) - .collect(Collectors.toList()) - ); + return factory.combine(promises, strict ? null : (_v) -> {}).timeout(timeout); } public static @NotNull Promise> combine(@NotNull List> promises, long timeout, PromiseFactory factory) { @@ -101,20 +54,7 @@ public class Promises { } public static @NotNull Promise all(@NotNull List> promises, PromiseFactory factory) { - if (promises.isEmpty()) return factory.resolve(null); - - Promise promise = factory.unresolved(); - for (Promise p : promises) { - p.addListener((ctx) -> { - if (ctx.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx.getException()); - } else if (promises.stream().allMatch(Promise::isCompleted)) { - promise.complete(null); - } - }); - } - return promise; + return factory.all(promises); } public static @NotNull Promise> combine(@NotNull Collection keys, @NotNull ExceptionalFunction mapper, long timeout, boolean strict, PromiseFactory factory) { @@ -136,17 +76,7 @@ public class Promises { } public static @NotNull Promise erase(@NotNull Promise p, PromiseFactory factory) { - Promise promise = factory.unresolved(); - p.addListener(ctx -> { - if (ctx.isError()) { - //noinspection ConstantConditions - promise.completeExceptionally(ctx.getException()); - } else { - promise.complete(null); - } - }); - - return promise; + return factory.erase(p); } public static @NotNull Promise erase(@NotNull Promise p) { @@ -154,16 +84,7 @@ public class Promises { } public static @NotNull Promise wrap(@NotNull CompletableFuture future, PromiseFactory factory) { - Promise promise = factory.unresolved(); - future.whenComplete((result, e) -> { - if (e != null) { - promise.completeExceptionally(e); - } else { - promise.complete(result); - } - }); - - return promise; + return factory.wrap(future); } } \ No newline at end of file diff --git a/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java b/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java new file mode 100644 index 0000000..b403a7c --- /dev/null +++ b/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java @@ -0,0 +1,48 @@ +package dev.tommyjs.futur; + +import dev.tommyjs.futur.executor.PromiseExecutor; +import dev.tommyjs.futur.executor.SinglePoolExecutor; +import dev.tommyjs.futur.impl.SimplePromiseFactory; +import dev.tommyjs.futur.promise.PromiseFactory; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +import java.util.Objects; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public final class PromiseTests { + + private final Logger logger = LoggerFactory.getLogger(PromiseTests.class); + private final PromiseExecutor> executor = SinglePoolExecutor.create(1); + private final PromiseFactory pfac = new SimplePromiseFactory<>(executor, logger); + + @Test + void testMono() { + Exception value = new Exception("Test Error"); + + var error = pfac.wrap(Mono.error(value)); + assert Objects.requireNonNull(error.getCompletion()).isError(); + assert error.getCompletion().getException() == value; + + var resolved = pfac.wrap(Mono.just(value)); + assert !Objects.requireNonNull(resolved.getCompletion()).isError(); + assert resolved.getCompletion().getResult() == value; + } + + @Test + void testErrorCancellation() throws InterruptedException { + var finish = new AtomicBoolean(); + pfac.start() + .thenRunDelayedAsync(() -> finish.set(true), 50, TimeUnit.MILLISECONDS) + .thenRunAsync(() -> {}) + .cancel(); + + Thread.sleep(100L); + assert !finish.get(); + } + +} diff --git a/futur-reactor/.gitignore b/futur-reactor/.gitignore deleted file mode 100644 index b63da45..0000000 --- a/futur-reactor/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/futur-reactor/build.gradle b/futur-reactor/build.gradle deleted file mode 100644 index 42dc278..0000000 --- a/futur-reactor/build.gradle +++ /dev/null @@ -1,5 +0,0 @@ -dependencies { - compileOnly(project(":futur-api")) - implementation("org.jetbrains:annotations:24.1.0") - implementation("io.projectreactor:reactor-core:3.6.4") -} \ No newline at end of file diff --git a/futur-reactor/src/main/java/dev/tommyjs/futur/reactor/ReactorTransformer.java b/futur-reactor/src/main/java/dev/tommyjs/futur/reactor/ReactorTransformer.java deleted file mode 100644 index 0683dab..0000000 --- a/futur-reactor/src/main/java/dev/tommyjs/futur/reactor/ReactorTransformer.java +++ /dev/null @@ -1,16 +0,0 @@ -package dev.tommyjs.futur.reactor; - -import dev.tommyjs.futur.promise.Promise; -import dev.tommyjs.futur.promise.PromiseFactory; -import org.jetbrains.annotations.NotNull; -import reactor.core.publisher.Mono; - -public class ReactorTransformer { - - public static @NotNull Promise wrapMono(@NotNull Mono mono, PromiseFactory factory) { - Promise promise = factory.unresolved(); - mono.subscribe(promise::complete, promise::completeExceptionally); - return promise; - } - -} diff --git a/futur-static/build.gradle b/futur-static/build.gradle new file mode 100644 index 0000000..a60942c --- /dev/null +++ b/futur-static/build.gradle @@ -0,0 +1,6 @@ +apply plugin: 'java-library' + +dependencies { + api project(':futur-api') + testImplementation project(':futur-api') +} \ No newline at end of file diff --git a/futur-static/src/main/java/dev/tommyjs/futur/lazy/PromiseUtil.java b/futur-static/src/main/java/dev/tommyjs/futur/lazy/PromiseUtil.java new file mode 100644 index 0000000..3c86ffc --- /dev/null +++ b/futur-static/src/main/java/dev/tommyjs/futur/lazy/PromiseUtil.java @@ -0,0 +1,93 @@ +package dev.tommyjs.futur.lazy; + +import dev.tommyjs.futur.promise.Promise; +import dev.tommyjs.futur.promise.PromiseCompletion; +import dev.tommyjs.futur.promise.PromiseFactory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public final class PromiseUtil { + + private static PromiseFactory pfac = StaticPromiseFactory.INSTANCE; + + public static @NotNull Logger getLogger() { + return pfac.getLogger(); + } + + public static void setPromiseFactory(PromiseFactory pfac) { + PromiseUtil.pfac = pfac; + } + + public static @NotNull Promise unresolved() { + return pfac.unresolved(); + } + + public static @NotNull Promise> combine(@NotNull Promise p1, @NotNull Promise p2) { + return pfac.combine(p1, p2); + } + + public static @NotNull Promise> combine(@NotNull Map> promises, @Nullable BiConsumer exceptionHandler) { + return pfac.combine(promises, exceptionHandler); + } + + public static @NotNull Promise> combine(@NotNull Map> promises) { + return pfac.combine(promises); + } + + public static @NotNull Promise> combine(@NotNull Iterable> promises, @Nullable Consumer exceptionHandler) { + return pfac.combine(promises, exceptionHandler); + } + + public static @NotNull Promise> combine(@NotNull Iterable> promises) { + return pfac.combine(promises); + } + + public static @NotNull Promise>> allSettled(@NotNull Iterable> promiseIterable) { + return pfac.allSettled(promiseIterable); + } + + public static @NotNull Promise>> allSettled(@NotNull Promise... promiseArray) { + return pfac.allSettled(promiseArray); + } + + public static @NotNull Promise all(@NotNull Iterable> promiseIterable) { + return pfac.all(promiseIterable); + } + + public static @NotNull Promise all(@NotNull Promise... promiseArray) { + return pfac.all(promiseArray); + } + + public static @NotNull Promise wrap(@NotNull CompletableFuture future) { + return pfac.wrap(future); + } + + public static @NotNull Promise wrap(@NotNull Mono mono) { + return pfac.wrap(mono); + } + + public static @NotNull Promise resolve(T value) { + return pfac.resolve(value); + } + + public static @NotNull Promise error(@NotNull Throwable error) { + return pfac.error(error); + } + + public static @NotNull Promise erase(@NotNull Promise p) { + return pfac.erase(p); + } + + public static @NotNull Promise start() { + return pfac.start(); + } + +} diff --git a/futur-static/src/main/java/dev/tommyjs/futur/lazy/StaticPromiseFactory.java b/futur-static/src/main/java/dev/tommyjs/futur/lazy/StaticPromiseFactory.java new file mode 100644 index 0000000..e61908f --- /dev/null +++ b/futur-static/src/main/java/dev/tommyjs/futur/lazy/StaticPromiseFactory.java @@ -0,0 +1,39 @@ +package dev.tommyjs.futur.lazy; + +import dev.tommyjs.futur.executor.PromiseExecutor; +import dev.tommyjs.futur.executor.SinglePoolExecutor; +import dev.tommyjs.futur.impl.SimplePromise; +import dev.tommyjs.futur.promise.AbstractPromiseFactory; +import dev.tommyjs.futur.promise.Promise; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ScheduledFuture; + +public final class StaticPromiseFactory extends AbstractPromiseFactory> { + + public final static StaticPromiseFactory INSTANCE = new StaticPromiseFactory(); + private final static @NotNull SinglePoolExecutor EXECUTOR = SinglePoolExecutor.create(1); + private final static @NotNull Logger LOGGER = LoggerFactory.getLogger(StaticPromiseFactory.class); + + private StaticPromiseFactory() { + + } + + @Override + public @NotNull Promise unresolved() { + return new SimplePromise<>(this); + } + + @Override + public @NotNull Logger getLogger() { + return LOGGER; + } + + @Override + public @NotNull PromiseExecutor> getExecutor() { + return EXECUTOR; + } + +} diff --git a/settings.gradle b/settings.gradle index 025feb7..9d69470 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ rootProject.name = 'futur' include 'futur-api' -include 'futur-reactor' +include 'futur-static'