Back
ModuleManagerImpl.java
javasrc/main/java/com/scriptslab/core/module/ModuleManagerImpl.java
package com.scriptslab.core.module;
import com.scriptslab.api.module.Module;
import com.scriptslab.api.module.ModuleManager;
import com.scriptslab.api.module.ModuleDescriptor;
import com.scriptslab.core.module.BaseModule;
import com.scriptslab.core.module.DependencyResolver;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Thread-safe implementation of ModuleManager.
*/
public final class ModuleManagerImpl implements ModuleManager {
private final Path modulesDirectory;
private final Logger logger;
private final Map<String, Module> modules;
private final Map<String, ModuleDescriptor> descriptors;
private final DependencyResolver dependencyResolver;
public ModuleManagerImpl(Path modulesDirectory) {
this.modulesDirectory = modulesDirectory;
this.logger = Logger.getLogger("ModuleManager");
this.modules = new ConcurrentHashMap<>();
this.descriptors = new ConcurrentHashMap<>();
this.dependencyResolver = new DependencyResolver();
// Create modules directory
modulesDirectory.toFile().mkdirs();
}
@Override
public CompletableFuture<Integer> loadAllModules() {
return CompletableFuture.supplyAsync(() -> {
logger.info("Loading modules from " + modulesDirectory);
File[] moduleFiles = modulesDirectory.toFile().listFiles();
if (moduleFiles == null || moduleFiles.length == 0) {
logger.info("No modules found");
return 0;
}
// Load all module descriptors first
List<ModuleDescriptor> allDescriptors = new ArrayList<>();
for (File file : moduleFiles) {
if (file.isDirectory()) {
try {
ModuleDescriptor descriptor = loadDescriptor(file.toPath());
allDescriptors.add(descriptor);
descriptors.put(descriptor.id(), descriptor);
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to load descriptor from " + file.getName(), e);
}
}
}
// Validate dependencies
try {
dependencyResolver.validateDependencies(allDescriptors);
} catch (DependencyResolver.MissingDependencyException e) {
logger.severe(e.getMessage());
return 0;
}
// Resolve load order
List<String> loadOrder;
try {
loadOrder = dependencyResolver.resolveLoadOrder(allDescriptors);
} catch (DependencyResolver.CyclicDependencyException e) {
logger.severe(e.getMessage());
return 0;
}
logger.info("Module load order: " + loadOrder);
// Load modules in order
int loaded = 0;
for (String moduleId : loadOrder) {
ModuleDescriptor descriptor = descriptors.get(moduleId);
if (descriptor != null && descriptor.enabled()) {
try {
Path modulePath = modulesDirectory.resolve(moduleId);
loadModule(modulePath).join();
loaded++;
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to load module " + moduleId, e);
}
}
}
logger.info("Loaded " + loaded + " modules");
return loaded;
});
}
@Override
public CompletableFuture<Module> loadModule(Path modulePath) {
return CompletableFuture.supplyAsync(() -> {
try {
// Load descriptor
ModuleDescriptor descriptor = loadDescriptor(modulePath);
descriptor.validate();
// Check if already loaded
if (modules.containsKey(descriptor.id())) {
logger.warning("Module " + descriptor.id() + " is already loaded");
return modules.get(descriptor.id());
}
// Create module instance
Module module = createModuleInstance(descriptor, modulePath);
// Load the module
module.onLoad().join();
modules.put(descriptor.id(), module);
logger.info("Loaded module: " + descriptor.name() + " v" + descriptor.version());
return module;
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to load module from " + modulePath, e);
throw new RuntimeException(e);
}
});
}
@Override
public CompletableFuture<Void> enableModule(String moduleId) {
return CompletableFuture.runAsync(() -> {
Module module = modules.get(moduleId);
if (module == null) {
throw new IllegalArgumentException("Module not found: " + moduleId);
}
if (module.isEnabled()) {
logger.warning("Module " + moduleId + " is already enabled");
return;
}
try {
module.onEnable().join();
logger.info("Enabled module: " + module.getName());
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to enable module " + moduleId, e);
throw new RuntimeException(e);
}
});
}
@Override
public CompletableFuture<Void> disableModule(String moduleId) {
return CompletableFuture.runAsync(() -> {
Module module = modules.get(moduleId);
if (module == null) {
throw new IllegalArgumentException("Module not found: " + moduleId);
}
if (!module.isEnabled()) {
logger.warning("Module " + moduleId + " is already disabled");
return;
}
try {
module.onDisable().join();
logger.info("Disabled module: " + module.getName());
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to disable module " + moduleId, e);
throw new RuntimeException(e);
}
});
}
@Override
public CompletableFuture<Void> reloadModule(String moduleId) {
return CompletableFuture.runAsync(() -> {
Module module = modules.get(moduleId);
if (module == null) {
throw new IllegalArgumentException("Module not found: " + moduleId);
}
try {
module.onReload().join();
logger.info("Reloaded module: " + module.getName());
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to reload module " + moduleId, e);
throw new RuntimeException(e);
}
});
}
@Override
public CompletableFuture<Void> unloadModule(String moduleId) {
return CompletableFuture.runAsync(() -> {
Module module = modules.get(moduleId);
if (module == null) {
throw new IllegalArgumentException("Module not found: " + moduleId);
}
try {
if (module.isEnabled()) {
module.onDisable().join();
}
modules.remove(moduleId);
descriptors.remove(moduleId);
logger.info("Unloaded module: " + module.getName());
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to unload module " + moduleId, e);
throw new RuntimeException(e);
}
});
}
@Override
public Optional<Module> getModule(String moduleId) {
return Optional.ofNullable(modules.get(moduleId));
}
@Override
public Collection<Module> getModules() {
return Collections.unmodifiableCollection(modules.values());
}
@Override
public Collection<Module> getEnabledModules() {
return modules.values().stream()
.filter(Module::isEnabled)
.toList();
}
@Override
public boolean isLoaded(String moduleId) {
return modules.containsKey(moduleId);
}
@Override
public boolean isEnabled(String moduleId) {
Module module = modules.get(moduleId);
return module != null && module.isEnabled();
}
@Override
public Path getModulesDirectory() {
return modulesDirectory;
}
@Override
public List<String> resolveLoadOrder(Collection<ModuleDescriptor> moduleDescriptors) {
return dependencyResolver.resolveLoadOrder(moduleDescriptors);
}
/**
* Loads module descriptor from module.yml.
*/
private ModuleDescriptor loadDescriptor(Path modulePath) {
File descriptorFile = modulePath.resolve("module.yml").toFile();
if (!descriptorFile.exists()) {
throw new IllegalStateException("module.yml not found in " + modulePath);
}
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(descriptorFile);
String id = yaml.getString("id");
String name = yaml.getString("name", id);
String version = yaml.getString("version", "1.0.0");
String description = yaml.getString("description", "");
List<String> authors = yaml.getStringList("authors");
Set<String> dependencies = new HashSet<>(yaml.getStringList("dependencies"));
Set<String> softDependencies = new HashSet<>(yaml.getStringList("soft-dependencies"));
String loadOrderStr = yaml.getString("load-order", "STARTUP");
ModuleDescriptor.LoadOrder loadOrder = ModuleDescriptor.LoadOrder.valueOf(loadOrderStr.toUpperCase());
boolean enabled = yaml.getBoolean("enabled", true);
return new ModuleDescriptor(
id, name, version, description, authors,
dependencies, softDependencies, loadOrder, enabled
);
}
/**
* Creates a module instance.
* For now, uses reflection to instantiate built-in modules.
* In production, this would load from JAR files.
*/
private Module createModuleInstance(ModuleDescriptor descriptor, Path modulePath) {
// Try to load built-in module class
String className = "com.myplugin.modules." + descriptor.id() + "." +
capitalize(descriptor.id()) + "Module";
try {
Class<?> moduleClass = Class.forName(className);
return (Module) moduleClass
.getConstructor(ModuleDescriptor.class, Path.class)
.newInstance(descriptor, modulePath);
} catch (ClassNotFoundException e) {
// Module class not found, create generic module
logger.warning("Module class not found: " + className + ", using generic module");
return new GenericModule(descriptor, modulePath);
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to instantiate module " + descriptor.id(), e);
throw new RuntimeException(e);
}
}
private String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
/**
* Generic module implementation for modules without custom class.
*/
private static class GenericModule extends BaseModule {
GenericModule(ModuleDescriptor descriptor, Path dataFolder) {
super(descriptor, dataFolder);
}
}
}