[FEATURE][EXPERIMENTAL] Базовая имплементация LauncherModulesManager

This commit is contained in:
Gravit 2019-08-26 16:27:30 +07:00
parent efa1bf0dd7
commit 1646d8b473
No known key found for this signature in database
GPG key ID: 061981E1E85D3216
9 changed files with 253 additions and 13 deletions

View file

@ -10,12 +10,16 @@ public abstract class LauncherModule {
protected LauncherModulesManager modulesManager; protected LauncherModulesManager modulesManager;
protected final LauncherModuleInfo moduleInfo; protected final LauncherModuleInfo moduleInfo;
protected ModulesConfigManager modulesConfigManager; protected ModulesConfigManager modulesConfigManager;
InitPhase initPhase = InitPhase.CREATED; protected InitPhase initPhase = InitPhase.CREATED;
protected LauncherModule() { protected LauncherModule() {
moduleInfo = new LauncherModuleInfo("UnknownModule"); moduleInfo = new LauncherModuleInfo("UnknownModule");
} }
public LauncherModuleInfo getModuleInfo() {
return moduleInfo;
}
public enum InitPhase public enum InitPhase
{ {
CREATED, CREATED,
@ -38,32 +42,41 @@ public Event cancel() {
return this; return this;
} }
protected boolean cancel; protected boolean cancel = false;
} }
public InitPhase getInitPhase() { public InitPhase getInitPhase() {
return initPhase; 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"); if(this.context != null) throw new IllegalStateException("Module already set context");
this.context = context; this.context = context;
this.modulesManager = context.getModulesManager(); this.modulesManager = context.getModulesManager();
this.modulesConfigManager = context.getModulesConfigManager(); this.modulesConfigManager = context.getModulesConfigManager();
return eventMap; }
public void preInit() {
//NOP
} }
public abstract void init(); 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); eventMap.put(tClass, handle);
return true; return true;
} }
@SuppressWarnings("unchecked") @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(); Class<? extends Event> tClass = event.getClass();
for(Map.Entry<Class<? extends Event>, EventHandler> e : eventMap.entrySet()) for(Map.Entry<Class<? extends Event>, EventHandler> e : eventMap.entrySet())

View file

@ -2,11 +2,15 @@
import pro.gravit.utils.Version; import pro.gravit.utils.Version;
import java.io.IOException;
import java.nio.file.Path;
import java.util.function.Predicate; import java.util.function.Predicate;
public interface LauncherModulesManager { public interface LauncherModulesManager {
LauncherModule loadModule(LauncherModule module); LauncherModule loadModule(LauncherModule module);
LauncherModule loadModule(Path file) throws IOException;
LauncherModule getModule(String name); LauncherModule getModule(String name);
LauncherModule getCoreModule();
default boolean containsModule(String name) default boolean containsModule(String name)
{ {
return getModule(name) != null; return getModule(name) != null;

View file

@ -2,5 +2,5 @@
import pro.gravit.launcher.modules.LauncherModule; import pro.gravit.launcher.modules.LauncherModule;
public interface InitEvent extends LauncherModule.Event { public class InitPhase extends LauncherModule.Event {
} }

View file

@ -1,4 +0,0 @@
package pro.gravit.launcher.modules.events;
public interface PostInitEvent {
}

View file

@ -2,5 +2,5 @@
import pro.gravit.launcher.modules.LauncherModule; import pro.gravit.launcher.modules.LauncherModule;
public interface PreInitEvent extends LauncherModule.Event { public class PostInitPhase extends LauncherModule.Event {
} }

View file

@ -2,5 +2,5 @@
import pro.gravit.launcher.modules.LauncherModule; import pro.gravit.launcher.modules.LauncherModule;
public interface WrapperEvent extends LauncherModule.Event { public class PreConfigPhase extends LauncherModule.Event {
} }

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}