Skip to content
This repository has been archived by the owner on Mar 11, 2024. It is now read-only.

Commit

Permalink
Async Modules
Browse files Browse the repository at this point in the history
  • Loading branch information
BelgianDev committed Dec 30, 2023
1 parent 24db93e commit cd8f1a1
Show file tree
Hide file tree
Showing 23 changed files with 119 additions and 769 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public interface AtlasNetworkServer {
* @return future action to notify your code when your task finishes, of fails.
*/
@CanIgnoreReturnValue
<V> FutureAction<V> createAsyncSupplier(Module module, Supplier<V> supplier);
<V> FutureAction<V> createAsyncSupplier(Supplier<V> supplier);

/**
* Creates an asynchronous task using the system's Executor Pool.
Expand All @@ -84,7 +84,7 @@ public interface AtlasNetworkServer {
* @return future action to notify your code when your task finishes, or fails.
*/
@CanIgnoreReturnValue
FutureAction<Void> createAsyncTask(Module module, Runnable runnable);
FutureAction<Void> createAsyncTask(Runnable runnable);

/**
* Creates an asynchronous task that will run every specified time interval.
Expand All @@ -95,5 +95,5 @@ public interface AtlasNetworkServer {
* @return future action, this action will never successfully complete, it can only fail or be cancelled.
*/
@CanIgnoreReturnValue
FutureAction<Void> createSequenceTask(Module module, Runnable runnable, long interval, TimeUnit intervalUnit);
FutureAction<Void> createSequenceTask(Runnable runnable, long interval, TimeUnit intervalUnit);
}
114 changes: 57 additions & 57 deletions api/src/main/java/fr/atlasworld/network/api/NetworkModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import fr.atlasworld.network.api.module.Module;
import fr.atlasworld.network.api.module.ModuleMeta;
import fr.atlasworld.network.api.module.ModuleStatus;
import fr.atlasworld.network.api.module.lifecycle.ModuleActivationContext;
import fr.atlasworld.network.api.module.lifecycle.ModuleDeactivationContext;
import fr.atlasworld.network.api.module.exception.unchecked.ExecutionModuleException;
import fr.atlasworld.network.api.module.lifecycle.ModuleUnloadContext;
import fr.atlasworld.network.api.module.lifecycle.ModuleLoadContext;
import fr.atlasworld.network.api.util.archive.JarArchive;
import org.jetbrains.annotations.ApiStatus;
Expand All @@ -19,21 +19,25 @@
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.jar.JarEntry;

/**
* The NetworkModule class is an abstract class that represents a network module.
* It provides basic functionality for initializing, activating, deactivating, and managing the module's lifecycle.
*/
public abstract class NetworkModule implements Module {
private final BlockingQueue<Runnable> operationQueue = new LinkedBlockingQueue<>();

private ModuleMeta meta = null;
private File file = null;
private JarArchive archive = null;
private ModuleClassLoader classLoader = null;
private ModuleStatus status = ModuleStatus.INACTIVE;
private Logger logger = null;
private boolean loaded = false;
private SimpleFutureAction<Void> deactivationFuture;
private boolean running = false;
private Thread thread;

@ApiStatus.Internal
public NetworkModule() {
Expand All @@ -45,25 +49,22 @@ public NetworkModule() {
}

/**
* This method is called before your module activates,
* use this if you need to load specific classes or files before your module activates.
* Your module code starts here, Register your listeners here.
* <p>
* WARNING: The system is far from being fully initialized. So use this with caution.
* WARNING: The system is far from being fully initialized.
* Many of the operation are not yet available.
* So don't start logic that interacts with the {@link AtlasNetworkServer} in this method.
* @param ctx load context.
*/
@Override
public void onLoad(@NotNull ModuleLoadContext ctx) {
protected void onLoad(@NotNull ModuleLoadContext ctx) {
}

/**
* Override this method, your module code starts here.
* Called when the system is shutting down,
* gracefully shutdown your module when this method is called.
* @param ctx unload context.
*/
protected void onActivate(@NotNull ModuleActivationContext ctx) {
}

/**
* Called when the system is shutting down
*/
protected void onDeactivate(@NotNull ModuleDeactivationContext ctx) {
protected void onUnload(@NotNull ModuleUnloadContext ctx) {
}

@NotNull
Expand All @@ -87,67 +88,66 @@ public final File getFile() {
return this.classLoader;
}

@NotNull
@Override
public final ModuleStatus getStatus() {
return this.status;
}

@ApiStatus.Internal
public final void initialize(@NotNull ModuleMeta meta, @NotNull File file, @NotNull JarArchive archive, @NotNull ModuleClassLoader classLoader, @NotNull Logger logger) {
if (this.loaded)
public final void initialize(@NotNull ModuleMeta meta, @NotNull File file, @NotNull JarArchive archive, @NotNull ModuleClassLoader classLoader, @NotNull Logger logger, ModuleLoadContext ctx) throws ExecutionModuleException {
if (this.running)
throw new UnsupportedOperationException("Module has already been initialized!");

this.meta = meta;
this.file = file;
this.archive = archive;
this.classLoader = classLoader;
this.logger = logger;

this.loaded = true;

this.deactivationFuture = new SimpleFutureAction<>();
}

@Override
@ApiStatus.Internal
public final void activate(@NotNull ModuleActivationContext ctx) {
if (this.status == ModuleStatus.ACTIVE)
throw new IllegalStateException("Module is already activated!");

if (this.deactivationFuture.isDone())
this.deactivationFuture = new SimpleFutureAction<>();
public final void start(ModuleLoadContext ctx) {
this.thread = Thread.currentThread();
this.running = true;

try {
this.onLoad(ctx);
} catch (Throwable throwable) {
throw new ExecutionModuleException(throwable);
}

this.status = ModuleStatus.ACTIVE;
this.onActivate(ctx);
}
while(this.running) {
try {
Runnable task = this.operationQueue.take();

@Override
@ApiStatus.Internal
public final void deactivate(@NotNull ModuleDeactivationContext ctx) {
if (this.status != ModuleStatus.ACTIVE)
throw new IllegalStateException("Module is already deactivated!");
task.run();
} catch (InterruptedException ignored) {

this.status = ModuleStatus.INACTIVE;
this.onDeactivate(ctx);
this.deactivationFuture.complete(null); // Trigger success listeners.
}

@Override
@ApiStatus.Internal
public final void crash(Throwable throwable) {
this.status = ModuleStatus.CRASHED;
this.logger.error("Oh no! I just crashed!", throwable);
this.deactivationFuture.fail(throwable); // Trigger failed listeners.
} catch (Throwable cause) {
this.logger.warn("Could not execute module task:", cause);
}
}
}

public final Logger getLogger() {
return this.logger;
}

@NotNull
@Override
public Thread getThread() {
return this.thread;
}

@Override
public final FutureAction<Void> deactivationFuture() {
return this.deactivationFuture;
public FutureAction<Void> execute(Runnable runnable) {
SimpleFutureAction<Void> future = new SimpleFutureAction<>();

this.operationQueue.add(() -> {
try {
runnable.run();
future.complete(null);
} catch (Throwable throwable) {
future.fail(throwable);
}
});

return future;
}

@Override
Expand Down
47 changes: 6 additions & 41 deletions api/src/main/java/fr/atlasworld/network/api/module/Module.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package fr.atlasworld.network.api.module;

import fr.atlasworld.network.api.concurrent.action.FutureAction;
import fr.atlasworld.network.api.module.lifecycle.ModuleActivationContext;
import fr.atlasworld.network.api.module.lifecycle.ModuleDeactivationContext;
import fr.atlasworld.network.api.module.lifecycle.ModuleLoadContext;
import fr.atlasworld.network.api.util.archive.Archive;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

import java.io.File;
Expand All @@ -16,7 +12,7 @@
public interface Module extends Archive {

/**
* Retrieve the configured meta information of the module.
* Retrieve the configured meta-information of the module.
*/
@NotNull ModuleMeta getMeta();

Expand All @@ -31,44 +27,13 @@ public interface Module extends Archive {
@NotNull ClassLoader getClassLoader();

/**
* Retrieve the module status.
* Retrieve the thread running this module.
*/
@NotNull ModuleStatus getStatus();
@NotNull Thread getThread();

/**
* Called when the module has been loaded,
* but before it has been set in an active state.
* <p>
* Call {@link #onLoad(ModuleLoadContext)} before calling {@link #activate(ModuleActivationContext)}
* Execute a task on the module thread.
* @param runnable task to execute.
*/
void onLoad(@NotNull ModuleLoadContext ctx);

/**
* Called when the module is set into an active state.
*/
@ApiStatus.Internal
void activate(@NotNull ModuleActivationContext ctx);

/**
* Called when the module is set into an inactive state.
*/
@ApiStatus.Internal
void deactivate(@NotNull ModuleDeactivationContext ctx);

/**
* Called when an uncaught exception was caught on the module.
*
* @param throwable cause
*/
@ApiStatus.Internal
void crash(Throwable throwable);

/**
* Retrieve the deactivation future of the module.
* Success listeners are called when the module deactivates normally.
* Fail listeners are called when the module crashes.
*
* @return null if the module is not yet activated.
*/
FutureAction<Void> deactivationFuture();
FutureAction<Void> execute(Runnable runnable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
/**
* Lifecycle context for when a module deactivates.
*/
public interface ModuleDeactivationContext {
public interface ModuleUnloadContext {
AtlasNetworkServer getServer();
}
50 changes: 4 additions & 46 deletions api/src/test/java/fr/atlasworld/network/api/test/TestModule.java
Original file line number Diff line number Diff line change
@@ -1,59 +1,17 @@
package fr.atlasworld.network.api.test;

import fr.atlasworld.network.api.NetworkModule;
import fr.atlasworld.network.api.file.FileManager;
import fr.atlasworld.network.api.file.configuration.ConfigurationReader;
import fr.atlasworld.network.api.module.Module;
import fr.atlasworld.network.api.module.lifecycle.ModuleActivationContext;
import fr.atlasworld.network.api.module.lifecycle.ModuleDeactivationContext;
import fr.atlasworld.network.api.module.lifecycle.ModuleLoadContext;
import fr.atlasworld.network.api.services.database.DatabaseService;
import fr.atlasworld.network.api.test.command.TestCommands;
import fr.atlasworld.network.api.test.configuration.TestConfiguration;
import fr.atlasworld.network.api.test.configuration.TestConfigurationSchema;
import fr.atlasworld.network.api.test.event.NetworkListener;
import fr.atlasworld.network.api.test.event.ServerListener;
import fr.atlasworld.network.api.test.services.database.LocalDatabaseService;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;

public class TestModule extends NetworkModule {
public static Logger LOGGER;
public static Module MODULE;

@Override
public void onLoad(@NotNull ModuleLoadContext ctx) {
ctx.getModuleClassLoader().useLibraryClassPath(true); // Makes sure the libraries can be used.
ctx.getModuleClassLoader().allowExternalClasspathUsage(false); // Test Module classes may only be used by itself.
try {
Thread.sleep(3000);
} catch (InterruptedException e) {}

LOGGER = this.getLogger();
MODULE = this;

LOGGER.info("Registering services..");
ctx.getServer().getServiceManager().registerService(this, DatabaseService.class, new LocalDatabaseService());
}

@Override
protected void onActivate(@NotNull ModuleActivationContext ctx) {
LOGGER.info("Module activated!");

FileManager fileManager = ctx.getServer().getFileManager();

ctx.getServer().getModuleManager().registerListener(this, new ServerListener());
ctx.getServer().getModuleManager().registerListener(this, new NetworkListener());
ctx.getServer().getModuleManager().registerListener(this, new TestCommands());

ConfigurationReader<TestConfiguration> testConfReader =
fileManager.registerConfiguration(new TestConfigurationSchema());

TestConfiguration configuration = testConfReader.readElseDefault(); // We try to read the file.
if (configuration.isPrintConsole()) {
LOGGER.info("Configuration file said I can say it here!");
}
}

@Override
protected void onDeactivate(@NotNull ModuleDeactivationContext ctx) {
LOGGER.info("Module deactivated!");
this.getLogger().info("I'm running on my own freaking thread ({})! Yeah baby!", getThread());
}
}

This file was deleted.

This file was deleted.

Loading

0 comments on commit cd8f1a1

Please sign in to comment.