mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 16:41:46 +03:00
[FEATURE][EXPERIMENTAL] Базовая имплементация LauncherModulesManager
This commit is contained in:
parent
efa1bf0dd7
commit
1646d8b473
9 changed files with 253 additions and 13 deletions
|
@ -10,12 +10,16 @@ public abstract class LauncherModule {
|
|||
protected LauncherModulesManager modulesManager;
|
||||
protected final LauncherModuleInfo moduleInfo;
|
||||
protected ModulesConfigManager modulesConfigManager;
|
||||
InitPhase initPhase = InitPhase.CREATED;
|
||||
protected InitPhase initPhase = InitPhase.CREATED;
|
||||
|
||||
protected LauncherModule() {
|
||||
moduleInfo = new LauncherModuleInfo("UnknownModule");
|
||||
}
|
||||
|
||||
public LauncherModuleInfo getModuleInfo() {
|
||||
return moduleInfo;
|
||||
}
|
||||
|
||||
public enum InitPhase
|
||||
{
|
||||
CREATED,
|
||||
|
@ -38,32 +42,41 @@ public Event cancel() {
|
|||
return this;
|
||||
}
|
||||
|
||||
protected boolean cancel;
|
||||
protected boolean cancel = false;
|
||||
}
|
||||
|
||||
public InitPhase getInitPhase() {
|
||||
return initPhase;
|
||||
}
|
||||
|
||||
Map<Class<? extends Event>, EventHandler> setContext(LauncherModulesContext context)
|
||||
public LauncherModule setInitPhase(InitPhase initPhase) {
|
||||
this.initPhase = initPhase;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setContext(LauncherModulesContext context)
|
||||
{
|
||||
if(this.context != null) throw new IllegalStateException("Module already set context");
|
||||
this.context = context;
|
||||
this.modulesManager = context.getModulesManager();
|
||||
this.modulesConfigManager = context.getModulesConfigManager();
|
||||
return eventMap;
|
||||
}
|
||||
|
||||
public void preInit() {
|
||||
//NOP
|
||||
}
|
||||
|
||||
public abstract void init();
|
||||
|
||||
<T extends Event> boolean registerEvent(EventHandler<T> handle, Class<T> tClass)
|
||||
|
||||
protected <T extends Event> boolean registerEvent(EventHandler<T> handle, Class<T> tClass)
|
||||
{
|
||||
eventMap.put(tClass, handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T extends Event> void callEvent(T event) throws Exception
|
||||
public final <T extends Event> void callEvent(T event) throws Exception
|
||||
{
|
||||
Class<? extends Event> tClass = event.getClass();
|
||||
for(Map.Entry<Class<? extends Event>, EventHandler> e : eventMap.entrySet())
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
import pro.gravit.utils.Version;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface LauncherModulesManager {
|
||||
LauncherModule loadModule(LauncherModule module);
|
||||
LauncherModule loadModule(Path file) throws IOException;
|
||||
LauncherModule getModule(String name);
|
||||
LauncherModule getCoreModule();
|
||||
default boolean containsModule(String name)
|
||||
{
|
||||
return getModule(name) != null;
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
import pro.gravit.launcher.modules.LauncherModule;
|
||||
|
||||
public interface InitEvent extends LauncherModule.Event {
|
||||
public class InitPhase extends LauncherModule.Event {
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package pro.gravit.launcher.modules.events;
|
||||
|
||||
public interface PostInitEvent {
|
||||
}
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
import pro.gravit.launcher.modules.LauncherModule;
|
||||
|
||||
public interface PreInitEvent extends LauncherModule.Event {
|
||||
public class PostInitPhase extends LauncherModule.Event {
|
||||
}
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
import pro.gravit.launcher.modules.LauncherModule;
|
||||
|
||||
public interface WrapperEvent extends LauncherModule.Event {
|
||||
public class PreConfigPhase extends LauncherModule.Event {
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package pro.gravit.launcher.modules.events;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import pro.gravit.launcher.modules.LauncherModule;
|
||||
|
||||
public class PreGsonPhase extends LauncherModule.Event {
|
||||
public GsonBuilder gsonBuilder;
|
||||
public GsonBuilder gsonConfigBuilder;
|
||||
|
||||
public PreGsonPhase(GsonBuilder gsonBuilder, GsonBuilder gsonConfigBuilder) {
|
||||
this.gsonBuilder = gsonBuilder;
|
||||
this.gsonConfigBuilder = gsonConfigBuilder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package pro.gravit.launcher.modules.impl;
|
||||
|
||||
import pro.gravit.launcher.modules.LauncherModulesContext;
|
||||
import pro.gravit.launcher.modules.LauncherModulesManager;
|
||||
import pro.gravit.launcher.modules.ModulesConfigManager;
|
||||
|
||||
public class SimpleModuleContext implements LauncherModulesContext {
|
||||
public final LauncherModulesManager modulesManager;
|
||||
public final ModulesConfigManager configManager;
|
||||
@Override
|
||||
public LauncherModulesManager getModulesManager() {
|
||||
return modulesManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModulesConfigManager getModulesConfigManager() {
|
||||
return configManager;
|
||||
}
|
||||
|
||||
public SimpleModuleContext(LauncherModulesManager modulesManager, ModulesConfigManager configManager) {
|
||||
this.modulesManager = modulesManager;
|
||||
this.configManager = configManager;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package pro.gravit.launcher.modules.impl;
|
||||
|
||||
import pro.gravit.launcher.managers.SimpleModulesConfigManager;
|
||||
import pro.gravit.launcher.modules.LauncherModule;
|
||||
import pro.gravit.launcher.modules.LauncherModuleInfo;
|
||||
import pro.gravit.launcher.modules.LauncherModulesManager;
|
||||
import pro.gravit.launcher.modules.ModulesConfigManager;
|
||||
import pro.gravit.utils.PublicURLClassLoader;
|
||||
import pro.gravit.utils.Version;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class SimpleModuleManager implements LauncherModulesManager {
|
||||
protected final List<LauncherModule> modules = new ArrayList<>();
|
||||
protected final List<String> moduleNames = new ArrayList<>();
|
||||
protected final SimpleModuleContext context;
|
||||
protected final ModulesConfigManager modulesConfigManager;
|
||||
|
||||
protected PublicURLClassLoader classLoader = new PublicURLClassLoader(new URL[]{});
|
||||
|
||||
protected final class ModulesVisitor extends SimpleFileVisitor<Path> {
|
||||
private ModulesVisitor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
loadModule(file);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public void autoload(Path dir) throws IOException {
|
||||
if (Files.notExists(dir)) Files.createDirectory(dir);
|
||||
else {
|
||||
IOHelper.walk(dir, new ModulesVisitor(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public void initModules() {
|
||||
List<LauncherModule> startedModules = Collections.unmodifiableList(new ArrayList<>(modules));
|
||||
for(LauncherModule module : startedModules)
|
||||
{
|
||||
module.preInit();
|
||||
}
|
||||
boolean isAnyModuleLoad = true;
|
||||
int modules_size = modules.size();
|
||||
int loaded = 0;
|
||||
while(isAnyModuleLoad)
|
||||
{
|
||||
isAnyModuleLoad = false;
|
||||
for(LauncherModule module : modules)
|
||||
{
|
||||
if(!module.getInitPhase().equals(LauncherModule.InitPhase.CREATED)) continue;
|
||||
if(checkDepend(module))
|
||||
{
|
||||
isAnyModuleLoad = true;
|
||||
module.setInitPhase(LauncherModule.InitPhase.INIT);
|
||||
module.init();
|
||||
module.setInitPhase(LauncherModule.InitPhase.FINISH);
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(modules_size == loaded) return;
|
||||
if(loaded > modules_size) throw new IllegalStateException(String.format("Module loading error. %d > %d", loaded, modules_size)); //WTF?
|
||||
for(LauncherModule module : modules)
|
||||
{
|
||||
if(module.getInitPhase().equals(LauncherModule.InitPhase.CREATED))
|
||||
{
|
||||
LauncherModuleInfo info = module.getModuleInfo();
|
||||
LogHelper.warning("Module %s required %s. Cyclic dependencies?", info.name, Arrays.toString(info.dependencies));
|
||||
module.setInitPhase(LauncherModule.InitPhase.INIT);
|
||||
module.init();
|
||||
module.setInitPhase(LauncherModule.InitPhase.FINISH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkDepend(LauncherModule module)
|
||||
{
|
||||
LauncherModuleInfo info = module.getModuleInfo();
|
||||
for(String dep : info.dependencies)
|
||||
{
|
||||
LauncherModule depModule = getModule(dep);
|
||||
if(depModule == null) throw new RuntimeException(String.format("Module %s required %s. %s not found", info.name, dep, dep));
|
||||
if(depModule.getInitPhase().equals(LauncherModule.InitPhase.CREATED)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public SimpleModuleManager(Path modulesDir, Path configDir) {
|
||||
modulesConfigManager = new SimpleModulesConfigManager(configDir);
|
||||
context = new SimpleModuleContext(this, modulesConfigManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LauncherModule loadModule(LauncherModule module) {
|
||||
modules.add(module);
|
||||
LauncherModuleInfo info = module.getModuleInfo();
|
||||
moduleNames.add(info.name);
|
||||
module.setContext(context);
|
||||
return module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LauncherModule loadModule(Path file) throws IOException {
|
||||
try (JarFile f = new JarFile(file.toFile())) {
|
||||
String moduleClass = f.getManifest().getMainAttributes().getValue("Module-Main-Class");
|
||||
if(moduleClass == null)
|
||||
{
|
||||
LogHelper.error("In module %s Module-Main-Class not found", file.toString());
|
||||
return null;
|
||||
}
|
||||
classLoader.addURL(file.toUri().toURL());
|
||||
LauncherModule module = (LauncherModule) Class.forName(moduleClass, true, classLoader).newInstance();
|
||||
loadModule(module);
|
||||
return module;
|
||||
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
|
||||
LogHelper.error(e);
|
||||
LogHelper.error("In module %s Module-Main-Class incorrect", file.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LauncherModule getModule(String name) {
|
||||
for(LauncherModule module : modules)
|
||||
{
|
||||
LauncherModuleInfo info = module.getModuleInfo();
|
||||
if(info.name.equals(name)) return module;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LauncherModule getCoreModule() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getModuleClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends LauncherModule> T getModule(Class<? extends T> clazz) {
|
||||
for(LauncherModule module : modules)
|
||||
{
|
||||
if(clazz.isAssignableFrom(module.getClass())) return (T) module;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends LauncherModule> T findModule(Class<? extends T> clazz, Predicate<Version> versionPredicate) {
|
||||
for(LauncherModule module : modules)
|
||||
{
|
||||
LauncherModuleInfo info = module.getModuleInfo();
|
||||
if(!versionPredicate.test(info.version)) continue;
|
||||
if(clazz.isAssignableFrom(module.getClass())) return (T) module;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends LauncherModule.Event> void invokeEvent(T event) throws Exception {
|
||||
for(LauncherModule module : modules)
|
||||
{
|
||||
module.callEvent(event);
|
||||
if(event.isCancel()) return;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue