Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion common/src/main/java/taboolib/common/TabooLib.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,14 @@ public Class<?> getClass(String name, boolean initialize, ClassLoader classLoade
* 执行生命周期任务
*/
public static void lifeCycle(LifeCycle lifeCycle) {
if (isStopped) {
if (isStopped && lifeCycle != LifeCycle.DISABLE) {
return;
}
// 检查 Kotlin 环境是否就绪
if (!TabooLib.isKotlinEnvironment()) {
if (lifeCycle == LifeCycle.DISABLE) {
return;
}
isStopped = true;
throw new RuntimeException(
t(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package taboolib.common;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class TabooLibDisableLifecycleTest {

@AfterEach
void restoreStoppedFlag() {
TabooLib.setStopped(false);
}

@Test
void disableLifecycleStillRunsWhenLoadingWasStopped() {
AtomicInteger calls = new AtomicInteger();
TabooLib.registerLifeCycleTask(LifeCycle.DISABLE, 0, calls::incrementAndGet);
TabooLib.setStopped(true);

TabooLib.lifeCycle(LifeCycle.DISABLE);

assertEquals(1, calls.get());
assertEquals(LifeCycle.DISABLE, TabooLib.getCurrentLifeCycle());
assertTrue(TabooLib.isStopped());
}
}
5 changes: 5 additions & 0 deletions platform/platform-afybroker/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ dependencies {
compileOnly(project(":common-platform-api"))
compileOnly("com.github.AfyerDev.AfyBroker:afybroker-server:f6261eab2a")
compileOnly("org.slf4j:slf4j-api:1.7.32")

testImplementation(project(":common"))
testImplementation(project(":common-util"))
testImplementation(project(":common-platform-api"))
testImplementation("com.github.AfyerDev.AfyBroker:afybroker-server:f6261eab2a")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package taboolib.platform;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;

final class AfyBrokerActiveGate {

private enum State {
OPEN,
ACTIVATING,
CLOSED
}

private final AtomicReference<State> state = new AtomicReference<>(State.OPEN);
private final CompletableFuture<Void> activationClosed = new CompletableFuture<>();

boolean activate(Runnable action) {
if (!state.compareAndSet(State.OPEN, State.ACTIVATING)) {
return false;
}
try {
action.run();
return true;
} finally {
state.set(State.CLOSED);
activationClosed.complete(null);
}
}

CompletableFuture<Void> close() {
if (state.compareAndSet(State.OPEN, State.CLOSED)) {
activationClosed.complete(null);
}
return activationClosed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import taboolib.common.platform.Plugin;

import java.io.File;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static taboolib.common.PrimitiveIO.t;

Expand All @@ -30,6 +32,8 @@ public class AfyBrokerPlugin extends net.afyer.afybroker.server.plugin.Plugin {
@Nullable
private static Plugin pluginInstance;
private static AfyBrokerPlugin instance;
private final AfyBrokerActiveGate activeGate = new AfyBrokerActiveGate();
private final AtomicBoolean disabled = new AtomicBoolean();

static {
PrimitiveIO.debug("AfyBroker 插件初始化完成,用时 {0} 毫秒。", TabooLib.execution(() -> {
Expand Down Expand Up @@ -107,25 +111,88 @@ public void onEnable() {
Broker.getScheduler().schedule(this, new Runnable() {
@Override
public void run() {
// 生命周期任务
TabooLib.lifeCycle(LifeCycle.ACTIVE);
// 调用 Plugin 实现的 onActive() 方法
if (pluginInstance != null) {
pluginInstance.onActive();
}
activeGate.activate(new Runnable() {
@Override
public void run() {
if (TabooLib.isStopped()) {
return;
}
// 生命周期任务
TabooLib.lifeCycle(LifeCycle.ACTIVE);
// 调用 Plugin 实现的 onActive() 方法
if (pluginInstance != null) {
pluginInstance.onActive();
}
}
});
}
}, 0, TimeUnit.MILLISECONDS);
}
}

@Override
public void onDisable() {
// 第一时间关闭激活入口;若 ACTIVE 正在执行,则在其结束后再进入 DISABLE
CompletableFuture<Void> activationClosed = activeGate.close();
if (activationClosed.isDone()) {
disable();
return;
}
activationClosed.whenComplete((unused, failure) -> {
if (failure != null) {
reportDisableFailure(failure);
return;
}
try {
disable();
} catch (Throwable ex) {
reportDisableFailure(ex);
}
});
}

private void disable() {
if (!disabled.compareAndSet(false, true)) {
return;
}
Throwable failure = null;
// 在插件未关闭的前提下,执行 onDisable() 方法
if (pluginInstance != null && !TabooLib.isStopped()) {
pluginInstance.onDisable();
try {
pluginInstance.onDisable();
} catch (Throwable ex) {
failure = ex;
}
}
// 生命周期任务
TabooLib.lifeCycle(LifeCycle.DISABLE);
// 生命周期任务必须执行,不能被用户回调异常跳过
try {
TabooLib.lifeCycle(LifeCycle.DISABLE);
} catch (Throwable ex) {
if (failure == null) {
failure = ex;
} else {
failure.addSuppressed(ex);
}
}
if (failure != null) {
AfyBrokerPlugin.<RuntimeException>rethrow(failure);
}
}

private void reportDisableFailure(Throwable ex) {
try {
PrimitiveIO.error("AfyBroker 平台禁用流程执行异常:{0}", ex.getMessage() == null ? ex.getClass().getName() : ex.getMessage());
} catch (Throwable ignored) {
}
try {
ex.printStackTrace();
} catch (Throwable ignored) {
}
}

@SuppressWarnings("unchecked")
private static <T extends Throwable> void rethrow(Throwable throwable) throws T {
throw (T) throwable;
}

@NotNull
Expand Down
Loading
Loading