Skip to content

Commit

Permalink
Add plugin source enum
Browse files Browse the repository at this point in the history
  • Loading branch information
serivesmejia committed Oct 26, 2024
1 parent 5331522 commit cbbb8c4
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 113 deletions.
Original file line number Diff line number Diff line change
@@ -1,110 +1,112 @@
/*
* Copyright (c) 2024 Sebastian Erives
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package com.github.serivesmejia.eocvsim.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;

/**
* A utility class for executing a Java process to run a main class within this project.
*/
public final class JavaProcess {

public interface ProcessIOReceiver {
void receive(InputStream in, InputStream err);
}

private JavaProcess() {}

/**
* Executes a Java process with the given class and arguments.
* @param klass the class to execute
* @param ioReceiver the receiver for the process' input and error streams (will use inheritIO if null)
* @param classpath the classpath to use
* (use System.getProperty("java.class.path") for the default classpath)
* @param jvmArgs the JVM arguments to pass to the process
* @param args the arguments to pass to the class
* @return the exit value of the process
* @throws InterruptedException if the process is interrupted
* @throws IOException if an I/O error occurs
*/
public static int execClasspath(Class klass, ProcessIOReceiver ioReceiver, String classpath, List<String> jvmArgs, List<String> args) throws InterruptedException, IOException {
String javaHome = System.getProperty("java.home");
String javaBin = javaHome +
File.separator + "bin" +
File.separator + "java";
String className = klass.getName();

List<String> command = new LinkedList<>();
command.add(javaBin);
if (jvmArgs != null) {
command.addAll(jvmArgs);
}
command.add("-cp");
command.add(classpath);
command.add(className);
if (args != null) {
command.addAll(args);
}

ProcessBuilder builder = new ProcessBuilder(command);

if (ioReceiver != null) {
Process process = builder.start();
ioReceiver.receive(process.getInputStream(), process.getErrorStream());
killOnExit(process);

process.waitFor();
return process.exitValue();
} else {
builder.inheritIO();
Process process = builder.start();
killOnExit(process);

process.waitFor();
return process.exitValue();
}
}

private static void killOnExit(Process process) {
Runtime.getRuntime().addShutdownHook(new Thread(process::destroy));
}

/**
* Executes a Java process with the given class and arguments.
* @param klass the class to execute
* @param jvmArgs the JVM arguments to pass to the process
* @param args the arguments to pass to the class
* @return the exit value of the process
* @throws InterruptedException if the process is interrupted
* @throws IOException if an I/O error occurs
*/
public static int exec(Class klass, List<String> jvmArgs, List<String> args) throws InterruptedException, IOException {
return execClasspath(klass, null, System.getProperty("java.class.path"), jvmArgs, args);
}

/*
* Copyright (c) 2024 Sebastian Erives
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package com.github.serivesmejia.eocvsim.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;

/**
* A utility class for executing a Java process to run a main class within this project.
*/
public final class JavaProcess {

public interface ProcessIOReceiver {
void receive(InputStream in, InputStream err);
}

private JavaProcess() {}

/**
* Executes a Java process with the given class and arguments.
* @param klass the class to execute
* @param ioReceiver the receiver for the process' input and error streams (will use inheritIO if null)
* @param classpath the classpath to use
* (use System.getProperty("java.class.path") for the default classpath)
* @param jvmArgs the JVM arguments to pass to the process
* @param args the arguments to pass to the class
* @return the exit value of the process
* @throws InterruptedException if the process is interrupted
* @throws IOException if an I/O error occurs
*/
public static int execClasspath(Class klass, ProcessIOReceiver ioReceiver, String classpath, List<String> jvmArgs, List<String> args) throws InterruptedException, IOException {
String javaHome = System.getProperty("java.home");
String javaBin = javaHome +
File.separator + "bin" +
File.separator + "java";
String className = klass.getName();

List<String> command = new LinkedList<>();
command.add(javaBin);
if (jvmArgs != null) {
command.addAll(jvmArgs);
}
command.add("-cp");
command.add(classpath);
command.add(className);
if (args != null) {
command.addAll(args);
}

System.out.println("Executing command: " + command);

ProcessBuilder builder = new ProcessBuilder(command);

if (ioReceiver != null) {
Process process = builder.start();
ioReceiver.receive(process.getInputStream(), process.getErrorStream());
killOnExit(process);

process.waitFor();
return process.exitValue();
} else {
builder.inheritIO();
Process process = builder.start();
killOnExit(process);

process.waitFor();
return process.exitValue();
}
}

private static void killOnExit(Process process) {
Runtime.getRuntime().addShutdownHook(new Thread(process::destroy));
}

/**
* Executes a Java process with the given class and arguments.
* @param klass the class to execute
* @param jvmArgs the JVM arguments to pass to the process
* @param args the arguments to pass to the class
* @return the exit value of the process
* @throws InterruptedException if the process is interrupted
* @throws IOException if an I/O error occurs
*/
public static int exec(Class klass, List<String> jvmArgs, List<String> args) throws InterruptedException, IOException {
return execClasspath(klass, null, System.getProperty("java.class.path"), jvmArgs, args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ abstract class EOCVSimPlugin {
*/
val fileSystem get() = context.fileSystem

/**
* Whether this plugin comes from a maven repository or a file
* This affects the classpath resolution and the way the plugin is loaded.
* @see io.github.deltacv.eocvsim.plugin.loader.PluginSource
*/
val pluginSource get() = context.pluginSource

/**
* The classpath of the plugin, additional to the JVM classpath
* This classpath is used to load classes from the plugin jar file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class PluginContext(
}

val plugin get() = loader.plugin
val pluginSource get() = loader.pluginSource
val classpath get() = loader.classpath

val hasSuperAccess get() = loader.hasSuperAccess
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ import net.lingala.zip4j.ZipFile
import java.io.File
import java.security.MessageDigest

enum class PluginSource {
REPOSITORY,
FILE
}

/**
* Loads a plugin from a jar file
* @param pluginFile the jar file of the plugin
Expand All @@ -45,6 +50,7 @@ import java.security.MessageDigest
class PluginLoader(
val pluginFile: File,
val classpath: List<File>,
val pluginSource: PluginSource,
val eocvSim: EOCVSim
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,13 @@ class PluginManager(val eocvSim: EOCVSim) {
}

for (pluginFile in pluginFiles) {
loaders[pluginFile] = PluginLoader(pluginFile, repositoryManager.classpath(), eocvSim)
loaders[pluginFile] = PluginLoader(
pluginFile,
repositoryManager.classpath(),
if(pluginFile in repositoryManager.resolvedFiles)
PluginSource.REPOSITORY else PluginSource.FILE,
eocvSim
)
}

isEnabled = true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
/*
* Copyright (c) 2024 Sebastian Erives
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package io.github.deltacv.eocvsim.plugin.repository

import com.github.serivesmejia.eocvsim.util.SysUtil
import com.github.serivesmejia.eocvsim.util.extension.hexString
import com.github.serivesmejia.eocvsim.util.extension.plus
import com.github.serivesmejia.eocvsim.util.loggerForThis
import com.moandjiezana.toml.Toml
import com.moandjiezana.toml.TomlWriter
import io.github.deltacv.eocvsim.plugin.loader.PluginManager
import org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem
import org.jboss.shrinkwrap.resolver.api.maven.Maven
Expand Down Expand Up @@ -33,6 +55,9 @@ class PluginRepositoryManager {

private lateinit var resolver: ConfigurableMavenResolverSystem

private val _resolvedFiles = mutableListOf<File>()
val resolvedFiles get() = _resolvedFiles.toList()

val logger by loggerForThis()

fun init() {
Expand All @@ -52,6 +77,7 @@ class PluginRepositoryManager {
?: Toml()

resolver = Maven.configureResolver()
.withClassPathResolution(false)

for (repo in pluginsRepositories.toMap()) {
if(repo.value !is String)
Expand Down Expand Up @@ -85,12 +111,14 @@ class PluginRepositoryManager {
try {
for(cached in cacheToml.toMap()) {
if(cached.key == pluginDep.hexString) {
logger.info("Found cached plugin dependency $pluginDep (${pluginDep.hexString})")

val cachedFile = File(cached.value as String)
if(cachedFile.exists()) {
logger.info("Found cached plugin dependency $pluginDep (${pluginDep.hexString})")
files += cachedFile
_resolvedFiles += cachedFile
continue
} else {
newCache.remove(cached.key)
}
}
}
Expand All @@ -107,6 +135,8 @@ class PluginRepositoryManager {
val destFile = File(MAVEN_FOLDER, file.name)
file.copyTo(destFile, true)

_resolvedFiles += destFile

newCache[pluginDep.hexString] = pluginJar!!.absolutePath
}

Expand Down

0 comments on commit cbbb8c4

Please sign in to comment.