Backport Velocity Polymer's async event API, with changes.
This commit backports the event manager from Velocity Polymer, with some changes for Velocity 1.1.x API compatibility: - All event handlers run asynchronously. (While EventTask.async() exists, it is not useful in 3.0.0, but is provided as a migration aid for Polymer.) - Event ordering is currently limited to the 5 levels available in Velocity 1.x.x.
This commit is contained in:
committed by
Andrew Steinborn
parent
3f50964f36
commit
821ca02ee7
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* The Velocity API is licensed under the terms of the MIT License. For more details,
|
||||
* reference the LICENSE file in the api top-level directory.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.api.event;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface AsyncEventExecutor<E> extends EventHandler<E> {
|
||||
|
||||
default void execute(E event) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This event handler can only be invoked asynchronously.");
|
||||
}
|
||||
|
||||
@Nullable EventTask executeAsync(E event);
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* The Velocity API is licensed under the terms of the MIT License. For more details,
|
||||
* reference the LICENSE file in the api top-level directory.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.api.event;
|
||||
|
||||
/**
|
||||
* Represents a continuation of a paused event handler. Any of the resume methods
|
||||
* may only be called once otherwise an {@link IllegalStateException} is expected.
|
||||
*/
|
||||
public interface Continuation {
|
||||
|
||||
/**
|
||||
* Resumes the continuation.
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* Resumes the continuation after the executed task failed.
|
||||
*/
|
||||
void resumeWithException(Throwable exception);
|
||||
}
|
@@ -7,12 +7,21 @@
|
||||
|
||||
package com.velocitypowered.api.event;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an interface to perform direct dispatch of an event. This makes integration easier to
|
||||
* achieve with platforms such as RxJava.
|
||||
* achieve with platforms such as RxJava. While this interface can be used to implement an
|
||||
* asynchronous event handler, {@link AsyncEventExecutor} provides a more idiomatic means of doing
|
||||
* so.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface EventHandler<E> {
|
||||
|
||||
void execute(E event);
|
||||
|
||||
default @Nullable EventTask executeAsync(E event) {
|
||||
execute(event);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
121
api/src/main/java/com/velocitypowered/api/event/EventTask.java
Normal file
121
api/src/main/java/com/velocitypowered/api/event/EventTask.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* The Velocity API is licensed under the terms of the MIT License. For more details,
|
||||
* reference the LICENSE file in the api top-level directory.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.api.event;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Represents a task that can be returned by a {@link EventHandler} which allows event handling to
|
||||
* be suspended and resumed at a later time, and executing event handlers completely or partially
|
||||
* asynchronously.
|
||||
*
|
||||
* <p><strong>Compatibility notice:</strong> While in Velocity 3.0.0, all event handlers still
|
||||
* execute asynchronously (to preserve backwards compatibility), this will not be the case in future
|
||||
* versions of Velocity. Please prepare your code by using continuations or returning an instance
|
||||
* returned by {@link #async(Runnable)}.</p>
|
||||
*/
|
||||
public interface EventTask {
|
||||
|
||||
/**
|
||||
* Whether this {@link EventTask} is required to be called asynchronously.
|
||||
*
|
||||
* <p>If this method returns {@code true}, the event task is guaranteed to be executed
|
||||
* asynchronously from the current thread. Otherwise, the event task may be executed on the
|
||||
* current thread or asynchronously.</p>
|
||||
*
|
||||
* @return Requires async
|
||||
*/
|
||||
boolean requiresAsync();
|
||||
|
||||
/**
|
||||
* Runs this event task with the given {@link Continuation}. The continuation must be notified
|
||||
* when the task is completed, either with {@link Continuation#resume()} if the task was
|
||||
* successful or {@link Continuation#resumeWithException(Throwable)} if an exception occurred.
|
||||
*
|
||||
* <p>The {@link Continuation} may only be resumed once, or an
|
||||
* {@link IllegalStateException} will be thrown.</p>
|
||||
*
|
||||
* <p>The {@link Continuation} doesn't need to be notified during the execution of this method,
|
||||
* this can happen at a later point in time and from another thread.</p>
|
||||
*
|
||||
* @param continuation The continuation
|
||||
*/
|
||||
void execute(Continuation continuation);
|
||||
|
||||
/**
|
||||
* Creates a basic async {@link EventTask} from the given {@link Runnable}. The task is guaranteed
|
||||
* to be executed asynchronously ({@link #requiresAsync()} always returns {@code true}).
|
||||
*
|
||||
* @param task The task
|
||||
* @return The async event task
|
||||
*/
|
||||
static EventTask async(final Runnable task) {
|
||||
requireNonNull(task, "task");
|
||||
return new EventTask() {
|
||||
|
||||
@Override
|
||||
public void execute(Continuation continuation) {
|
||||
task.run();
|
||||
continuation.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresAsync() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an continuation based {@link EventTask} from the given {@link Consumer}. The task isn't
|
||||
* guaranteed to be executed asynchronously ({@link #requiresAsync()} always returns
|
||||
* {@code false}).
|
||||
*
|
||||
* @param task The task to execute
|
||||
* @return The event task
|
||||
*/
|
||||
static EventTask withContinuation(final Consumer<Continuation> task) {
|
||||
requireNonNull(task, "task");
|
||||
return new EventTask() {
|
||||
|
||||
@Override
|
||||
public void execute(final Continuation continuation) {
|
||||
task.accept(continuation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresAsync() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an continuation based {@link EventTask} for the given {@link CompletableFuture}. The
|
||||
* continuation will be notified once the given future is completed.
|
||||
*
|
||||
* @param future The task to wait for
|
||||
* @return The event task
|
||||
*/
|
||||
// The Error Prone annotation here is spurious. The Future is handled via the CompletableFuture
|
||||
// API, which does NOT use the traditional blocking model.
|
||||
@SuppressWarnings("FutureReturnValueIgnored")
|
||||
static EventTask resumeWhenComplete(final CompletableFuture<?> future) {
|
||||
requireNonNull(future, "future");
|
||||
return withContinuation(continuation -> future.whenComplete((result, cause) -> {
|
||||
if (cause != null) {
|
||||
continuation.resumeWithException(cause);
|
||||
} else {
|
||||
continuation.resume();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
@@ -57,4 +57,16 @@ public interface PluginManager {
|
||||
* @throws UnsupportedOperationException if the operation is not applicable to this plugin
|
||||
*/
|
||||
void addToClasspath(Object plugin, Path path);
|
||||
|
||||
/**
|
||||
* Ensures a plugin container exists for the given {@code plugin}.
|
||||
*
|
||||
* @param plugin the instance to look up the container for
|
||||
* @return container for the plugin
|
||||
*/
|
||||
default PluginContainer ensurePluginContainer(Object plugin) {
|
||||
return this.fromInstance(plugin)
|
||||
.orElseThrow(() -> new IllegalArgumentException(plugin.getClass().getCanonicalName()
|
||||
+ " does not have a container."));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user