From b8bf70a178961c0c4ff353ebc3098d3ef8c874588c2b20f085377fe60862b28d Mon Sep 17 00:00:00 2001 From: Minimons Date: Tue, 16 Jun 2026 13:23:02 +0200 Subject: [PATCH] Added Nenjim Kicker (BROKEN!) --- .../r35157/nenjim/kicker/NenjimKicker.tjava | 27 +++++ .../r35157/nenjim/kicker/NenjimProcess.tjava | 14 +++ .../r35157/nenjim/kicker/impl/ref/Dummy.tjava | 20 ++++ .../nenjim/kicker/impl/ref/Initializer.tjava | 53 +++++++++ .../kicker/impl/ref/NenjimClassLoader.tjava | 46 ++++++++ .../kicker/impl/ref/NenjimKickerImpl.tjava | 107 ++++++++++++++++++ .../kicker/impl/ref/NenjimProcessImpl.tjava | 25 ++++ 7 files changed, 292 insertions(+) create mode 100644 src/main/tjava/com/r35157/nenjim/kicker/NenjimKicker.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/kicker/NenjimProcess.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/kicker/impl/ref/Dummy.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/kicker/impl/ref/Initializer.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimClassLoader.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimKickerImpl.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimProcessImpl.tjava diff --git a/src/main/tjava/com/r35157/nenjim/kicker/NenjimKicker.tjava b/src/main/tjava/com/r35157/nenjim/kicker/NenjimKicker.tjava new file mode 100644 index 0000000..8d63aa9 --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/kicker/NenjimKicker.tjava @@ -0,0 +1,27 @@ +package com.r35157.nenjim.kicker; + +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; + +public interface NenjimKicker { + /** + * Starts a process based on the provided fully qualified interface name. + * The actual implementation that is run is bound according to the Context. + * + * @param fqInterfaceName the fully qualified interface name for the actual class to start as a process. The interface must extend the {@code NenjimProcess} interface. + */ + void startProcess(@NotNull String fqInterfaceName); + + /** + * Provides a map of all running processes managed by the NenjimHub, keyed by the processId. + * + * @return a map of {@code NenjimProcess} running processes + */ + @NotNull HashMap getRunningProcesses(); + + /** + * A no-operation (noop). This method does nothing. + */ + void noop(); +} diff --git a/src/main/tjava/com/r35157/nenjim/kicker/NenjimProcess.tjava b/src/main/tjava/com/r35157/nenjim/kicker/NenjimProcess.tjava new file mode 100644 index 0000000..18c1922 --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/kicker/NenjimProcess.tjava @@ -0,0 +1,14 @@ +package com.r35157.nenjim.kicker; + +//import com.r35157.nenjim.hubd.Context; +import com.r35157.nenjim.hubd.NenjimHub; +import org.jetbrains.annotations.NotNull; + +public interface NenjimProcess { + void run() throws Exception; + + default void setContext(/*@NotNull Context context*/) {}; + default void setHub(@NotNull NenjimHub hub) {}; + + @NotNull String getName(); +} diff --git a/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/Dummy.tjava b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/Dummy.tjava new file mode 100644 index 0000000..65aa4eb --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/Dummy.tjava @@ -0,0 +1,20 @@ +package com.r35157.nenjim.kicker.impl.ref; + +import com.r35157.nenjim.kicker.NenjimProcess; +//import org.apache.logging.log4j.LogManager; +//import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +public final class Dummy implements NenjimProcess { + @Override + public void run() { + //log.error("Process '" + getName() + "' started."); + } + + @Override + public @NotNull String getName() { + return "Just A Dummy"; + } + + //private static final Logger log = LogManager.getLogger(Dummy.class); +} diff --git a/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/Initializer.tjava b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/Initializer.tjava new file mode 100644 index 0000000..31427c7 --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/Initializer.tjava @@ -0,0 +1,53 @@ +package com.r35157.nenjim.kicker.impl.ref; + +import com.r35157.nenjim.hubd.NenjimHub; +import com.r35157.nenjim.hubd.ctx.Context; +import com.r35157.nenjim.kicker.NenjimKicker; + +import java.nio.file.Files; +import java.nio.file.Path; + +public final class Initializer { + static void main() { + try { + Initializer bs = new Initializer(); + String classesCacheDirOrig = "~/.config/nenjim/kicker/cache/classes"; + String classesCacheDir = (classesCacheDirOrig.startsWith("~/")) + ? System.getProperty("user.home") + classesCacheDirOrig.substring(1) + : classesCacheDirOrig; + Path pathToClassesCache = Path.of(classesCacheDir); + + if(Files.exists(pathToClassesCache) == false) { + System.err.println("Cannot find '" + classesCacheDirOrig + "'"); + System.exit(-1); + } + + bs.startNenjimKicker(pathToClassesCache); + } catch(Throwable t) { + System.err.println("Error initializing NenjimKicker - " + t.getMessage()); + } + } + + public Initializer() { + } + + private void startNenjimKicker(Path pathToClassCache) throws Exception { + System.out.println("Initializing NenjimKicker..."); + + NenjimHub nenjimHub = null; + Context defaultContext = new Context(); + NenjimClassLoader nenjimClassLoader = new NenjimClassLoader(defaultContext, pathToClassCache); + + Class clazz = nenjimClassLoader.findClass("com.r35157.nenjim.kicker.impl.ref.NenjimKickerImpl"); + Object instance = clazz.getDeclaredConstructor().newInstance(); + NenjimKicker kicker = (NenjimKicker)instance; + + System.out.println(kicker); + System.out.println("Loaded class: " + clazz.getName()); + System.out.println("ClassLoader: " + clazz.getClassLoader()); + System.out.println("Parent: " + clazz.getClassLoader().getParent()); + System.out.println("Instance: " + instance); + + System.out.println("Done!"); + } +} diff --git a/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimClassLoader.tjava b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimClassLoader.tjava new file mode 100644 index 0000000..2738d81 --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimClassLoader.tjava @@ -0,0 +1,46 @@ +package com.r35157.nenjim.kicker.impl.ref; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import com.r35157.nenjim.hubd.ctx.Context; +import com.r35157.libs.valuetypes.basic.SemanticVersion; +//import com.r35157.nenjim.hubd.NenjimJournal; + +public final class NenjimClassLoader extends ClassLoader { + public NenjimClassLoader(Context context, Path classCacheRoot) { + this.context = context; + this.classCacheRoot = classCacheRoot; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + // Check if we have any local version preferences configured for the associated Context. + SemanticVersion version = context.getVersion(name); + + if(version == null) { + // No local configurations for a specific version has been configured for this package + // in this Context. Use the version the developer do recommend in the Journal. + version = null;//journal.getVersion(); + } + + System.out.println("Searching for class '" + name + "' in local class cache..."); + String relativePath = name.replace('.', '/') + ".class"; + Path classFile = classCacheRoot.resolve(relativePath); + + if (!Files.exists(classFile)) { + throw new ClassNotFoundException("Class not found: " + name + " at " + classFile); + } + + try { + byte[] classBytes = Files.readAllBytes(classFile); + return defineClass(name, classBytes, 0, classBytes.length); + } catch (IOException e) { + throw new ClassNotFoundException("Failed to load class: " + name, e); + } + } + + //private NenjimJournal journal; + private Context context; + private Path classCacheRoot; +} diff --git a/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimKickerImpl.tjava b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimKickerImpl.tjava new file mode 100644 index 0000000..801519a --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimKickerImpl.tjava @@ -0,0 +1,107 @@ +package com.r35157.nenjim.kicker.impl.ref; + +//import com.r35157.nenjim.hubd.Context; +import com.r35157.nenjim.kicker.NenjimKicker; +import com.r35157.nenjim.kicker.NenjimProcess; +//import org.apache.logging.log4j.LogManager; +//import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +public final class NenjimKickerImpl implements NenjimKicker { + + public NenjimKickerImpl() throws InterruptedException { + lock = new Object(); + running = 0; + processes = new ConcurrentHashMap<>(); + processThreads = new ConcurrentHashMap<>(); + nextProcessId = new AtomicInteger(1); + + startProcess("com.r35157.nenjim.kicker.impl.ref.Dummy"); + + //log.info("Waiting for all processes to finish!"); + awaitAllProcessesFinished(); + //log.info("Kicker successfully shutdown!"); + } + + @Override + public void startProcess(@NotNull String fqInterfaceName) { + ClassLoader loader = ClassLoader.getSystemClassLoader(); + int processId = nextProcessId.getAndIncrement(); + + final NenjimProcess proc; + final String procName; + + try { + Class clazz = loader.loadClass(fqInterfaceName); + + if (!NenjimProcess.class.isAssignableFrom(clazz)) { + throw new IllegalArgumentException("'" + fqInterfaceName + "' does NOT implement NenjimProcess"); + } + + proc = (NenjimProcess) clazz.getDeclaredConstructor().newInstance(); + procName = proc.getName(); + } catch (Throwable e) { + //log.error("Failed to create process '{}' (#{}).", fqInterfaceName, processId, e); + return; + } + + processes.put(processId, proc); + + //Context context = new Context(); log.warn("TODO: Set the Context here when we have one!"); + //proc.setContext(context); + //proc.setHub(null); log.warn("TODO: Set the HUB here when we have one!"); + + synchronized (lock) { + running++; + } + + Thread t = Thread.ofVirtual() + .name(procName) + .start(() -> { + try { + //log.info("Starting process '{}' (#{})...", procName, processId); + proc.run(); + } catch (Throwable e) { + //log.error("Process '{}' (#{}) crashed!", procName, processId, e); + } finally { + processThreads.remove(processId); + processes.remove(processId); + synchronized (lock) { + running--; + lock.notifyAll(); + } + } + }); + + processThreads.put(processId, t); + } + + @Override + public @NotNull HashMap getRunningProcesses() { + return new HashMap<>(processes); + } + + @Override + public void noop() { + } + + public void awaitAllProcessesFinished() throws InterruptedException { + synchronized (lock) { + while (running > 0) { + lock.wait(); + } + } + } + + //private static final Logger log = LogManager.getLogger(NenjimKickerImpl.class); + + private int running; + private final Object lock; + private final ConcurrentHashMap processes; + private final ConcurrentHashMap processThreads; + private final AtomicInteger nextProcessId; +} diff --git a/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimProcessImpl.tjava b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimProcessImpl.tjava new file mode 100644 index 0000000..692443e --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/kicker/impl/ref/NenjimProcessImpl.tjava @@ -0,0 +1,25 @@ +package com.r35157.nenjim.kicker.impl.ref; + +//import com.r35157.nenjim.hubd.Context; +import com.r35157.nenjim.hubd.NenjimHub; +import com.r35157.nenjim.kicker.NenjimProcess; +import org.jetbrains.annotations.NotNull; + +public final class NenjimProcessImpl implements NenjimProcess { + @Override + public void run() throws Exception { + } + + @Override + public void setContext(/*@NotNull Context context*/) { + } + + @Override + public void setHub(@NotNull NenjimHub nenjimHub) { + } + + @Override + public @NotNull String getName() { + return ""; + } +}