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

Commit

Permalink
Merge pull request #92 from Diruptio/feature/plugin/classtransformer
Browse files Browse the repository at this point in the history
Class Transformers
  • Loading branch information
FabiPunktExe authored Oct 21, 2024
2 parents 4576870 + 7ce9632 commit 924bce5
Show file tree
Hide file tree
Showing 18 changed files with 377 additions and 56 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group = "diruptio"
version = "0.2.0"
version = "0.3.0"

repositories {
mavenCentral()
Expand Down
24 changes: 24 additions & 0 deletions node/bom/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
plugins {
id("java")
}

repositories {
mavenCentral()
}

dependencies {
implementation(project(":node:loader"))
implementation(project(":node"))
}

tasks {
compileJava {
options.encoding = "UTF-8"
options.release = 17
}

jar {
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
}
5 changes: 3 additions & 2 deletions node/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ repositories {
}

dependencies {
implementation(project(":common"))
compileOnly(project(":common"))
compileOnly(project(":node:plugin-api"))
compileOnly("org.jetbrains:annotations:26.0.0")
implementation("commons-io:commons-io:2.17.0")
}
Expand Down Expand Up @@ -59,7 +60,7 @@ tasks {
jar {
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
duplicatesStrategy = DuplicatesStrategy.INCLUDE
archiveBaseName = "OCNode"
archiveFileName = "OCNode.jar"
manifest.attributes["Implementation-Title"] = "ObsidianCloud"
manifest.attributes["Implementation-Version"] = version
manifest.attributes["Main-Class"] = "de.obsidiancloud.node.ObsidianCloudNode"
Expand Down
55 changes: 55 additions & 0 deletions node/loader/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
plugins {
id("java")
id("application")
}

repositories {
mavenCentral()
}

dependencies {
implementation(project(":common"))
implementation(project(":node:plugin-api"))
compileOnly("org.jetbrains:annotations:25.0.0")
implementation("net.lenni0451.classtransform:core:1.14.0")
implementation("net.lenni0451.classtransform:additionalclassprovider:1.14.0")
implementation("net.lenni0451.classtransform:mixinstranslator:1.14.0")
implementation("net.lenni0451.classtransform:mixinsdummy:1.14.0")
}

val addJars =
tasks.register<Copy>("addJars") {
val nodeTask = project(":node").tasks.named("jar").get()
dependsOn(nodeTask)
from(nodeTask.outputs)
into(layout.buildDirectory.dir("generated/sources/resources"))
}
sourceSets.main.get().resources.srcDirs(addJars.map { it.outputs })

tasks {
compileJava {
dependsOn(addJars)
options.encoding = "UTF-8"
options.release = 17
}

jar {
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
duplicatesStrategy = DuplicatesStrategy.INCLUDE
archiveBaseName = "OCNode"
manifest.attributes["Implementation-Title"] = "ObsidianCloud"
manifest.attributes["Implementation-Version"] = version
manifest.attributes["Main-Class"] = "de.obsidiancloud.node.ObsidianCloudNodeLoader"
}

named<JavaExec>("run") {
workingDir = rootProject.file("run")
workingDir.mkdirs()
standardOutput = System.out
standardInput = System.`in`
}
}

application {
mainClass = "de.obsidiancloud.node.ObsidianCloudNodeLoader"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package de.obsidiancloud.node;

import de.obsidiancloud.node.plugin.DefaultPluginLoader;
import de.obsidiancloud.node.plugin.PluginLoader;
import de.obsidiancloud.node.util.SharedClassLoader;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import net.lenni0451.classtransform.TransformerManager;
import net.lenni0451.classtransform.additionalclassprovider.MutableBasicClassProvider;
import net.lenni0451.classtransform.mixinstranslator.MixinsTranslator;
import net.lenni0451.classtransform.utils.FailStrategy;
import net.lenni0451.classtransform.utils.loader.InjectionClassLoader;
import net.lenni0451.classtransform.utils.tree.IClassProvider;

public class ObsidianCloudNodeLoader {
private static final Logger logger = Logger.getLogger("loader");

public static void main(String[] args) {
try {
Files.createDirectories(Path.of("logs"));
LogManager.getLogManager()
.readConfiguration(
ObsidianCloudNodeLoader.class.getClassLoader().getResourceAsStream("logging.properties"));
logger.info("Loading the Node...");

InputStream inputStream = ObsidianCloudNodeLoader.class.getResourceAsStream("/OCNode.jar");
Path tempFile = Files.createTempFile("OCNode", ".jar");
tempFile.toFile().deleteOnExit();
Files.copy(Objects.requireNonNull(inputStream), tempFile, StandardCopyOption.REPLACE_EXISTING);
inputStream.close();

IClassProvider classProvider = new MutableBasicClassProvider(new SharedClassLoader());
TransformerManager transformerManager = new TransformerManager(classProvider);
transformerManager.setFailStrategy(FailStrategy.CONTINUE);
transformerManager.addTransformerPreprocessor(new MixinsTranslator());
InjectionClassLoader nodeClassLoader = new InjectionClassLoader(
transformerManager,
new SharedClassLoader(),
tempFile.toUri().toURL());

PluginLoader pluginLoader = new DefaultPluginLoader(transformerManager);
pluginLoader.loadPlugins();

Class<?> clazz = nodeClassLoader.loadClass("de.obsidiancloud.node.ObsidianCloudNode");
Field field = clazz.getDeclaredField("pluginLoader");
field.setAccessible(true);
field.set(null, pluginLoader);

logger.info("Launching the Node...");
Method method = clazz.getDeclaredMethod("main", String[].class);
method.invoke(null, (Object) args);
} catch (Throwable exception) {
logger.log(Level.SEVERE, "Failed to load the Node", exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package de.obsidiancloud.node.plugin;

import de.obsidiancloud.common.config.Config;
import de.obsidiancloud.node.util.SharedClassLoader;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
Expand All @@ -12,17 +12,24 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import net.lenni0451.classtransform.TransformerManager;
import org.jetbrains.annotations.NotNull;

public class DefaultPluginLoader implements PluginLoader {
private final Logger logger = Logger.getLogger("DefaultPluginLoader");
private final List<PluginInfo> pluginInfos = new ArrayList<>();
private final Map<PluginInfo, PluginClassLoader> classLoaders = new HashMap<>();
private final Map<PluginInfo, SharedClassLoader> classLoaders = new HashMap<>();
private final Map<PluginInfo, Class<?>> mainClasses = new HashMap<>();
private final Map<PluginInfo, Plugin> loadedPlugins = new HashMap<>();
private final TransformerManager transformerManager;

public DefaultPluginLoader(@NotNull TransformerManager transformerManager) {
this.transformerManager = transformerManager;
}

@Override
public void loadPlugins() {
logger.info("Loading plugins");
try {
Path pluginsDirectory = Path.of("plugins");
Stream<Path> stream = Files.list(pluginsDirectory);
Expand Down Expand Up @@ -113,9 +120,10 @@ private boolean loadPlugin(@NotNull PluginInfo info, @NotNull Stack<PluginInfo>
try {
logger.info("Loading plugin " + info.name());
Plugin plugin = (Plugin) constructor.newInstance();
plugin.info = info;
plugin.loader = this;
plugin.classLoader = classLoaders.get(info);
plugin.info = info;
plugin.transformerManager = transformerManager;
plugin.config = new Config(Path.of("plugins", info.name(), "config.yml"), Config.Type.YAML);
plugin.onLoad();
loadedPlugins.put(info, plugin);
Expand All @@ -129,8 +137,8 @@ private boolean loadPlugin(@NotNull PluginInfo info, @NotNull Stack<PluginInfo>

private void loadPluginInfo(@NotNull Path path) {
try (JarFile jarFile = new JarFile(path.toFile())) {
URL[] urls = new URL[] {new URL("jar:file:" + path.toAbsolutePath() + "!/")};
PluginClassLoader classLoader = new PluginClassLoader(urls);
URL url = new URL("jar:file:" + path.toAbsolutePath() + "!/");
SharedClassLoader classLoader = new SharedClassLoader(getClass().getClassLoader(), url);

Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
Expand All @@ -142,7 +150,6 @@ private void loadPluginInfo(@NotNull Path path) {
.substring(0, entry.getName().length() - 6)
.replace('/', '.');
Class<?> clazz = classLoader.loadClass(className);
classLoader.classes.add(clazz);
PluginInfo info = clazz.getAnnotation(PluginInfo.class);
if (Plugin.class.isAssignableFrom(clazz) && info != null) {
pluginInfos.add(info);
Expand All @@ -157,6 +164,7 @@ private void loadPluginInfo(@NotNull Path path) {

@Override
public void unloadPlugins() {
logger.info("Unloading plugins");
for (PluginInfo info : pluginInfos) {
unloadPlugin(info, new Stack<>());
}
Expand Down Expand Up @@ -197,40 +205,7 @@ private void unloadPlugin(@NotNull PluginInfo info, @NotNull Stack<PluginInfo> s
return loadedPlugins;
}

public @NotNull Map<PluginInfo, PluginClassLoader> getClassLoaders() {
public @NotNull Map<PluginInfo, SharedClassLoader> getClassLoaders() {
return classLoaders;
}

public class PluginClassLoader extends URLClassLoader {
List<Class<?>> classes = new ArrayList<>();

public PluginClassLoader(@NotNull URL[] urls) {
super(urls);
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
return internalLoadClass(name, resolve, true);
}

public Class<?> internalLoadClass(String name, boolean resolve, boolean checkOther)
throws ClassNotFoundException {
try {
return super.loadClass(name, resolve);
} catch (ClassNotFoundException exception) {
if (checkOther) {
for (PluginInfo info : loadedPlugins.keySet()) {
try {
PluginClassLoader classLoader = classLoaders.get(info);
if (classLoader != this) {
return classLoader.internalLoadClass(name, resolve, false);
}
} catch (Throwable ignored) {
}
}
}
throw exception;
}
}
}
}
12 changes: 12 additions & 0 deletions node/loader/src/main/resources/logging.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler

java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=de.obsidiancloud.common.logging.ConsoleFormatter
java.util.logging.ConsoleHandler.encoding=UTF-8

java.util.logging.FileHandler.level=INFO
java.util.logging.FileHandler.formatter=de.obsidiancloud.common.logging.FileFormatter
java.util.logging.FileHandler.encoding=UTF-8
java.util.logging.FileHandler.pattern=logs/node.log
java.util.logging.FileHandler.limit=50000
java.util.logging.FileHandler.count=50000
25 changes: 25 additions & 0 deletions node/plugin-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
id("java")
}

repositories {
mavenCentral()
}

dependencies {
compileOnly(project(":common"))
compileOnly("org.jetbrains:annotations:25.0.0")
compileOnly("net.lenni0451.classtransform:core:1.14.0")
}

tasks {
compileJava {
options.encoding = "UTF-8"
options.release = 17
}

jar {
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
}
Loading

0 comments on commit 924bce5

Please sign in to comment.