SourceRunner is a lightweight Java library that allows you to compile and run Java code directly from a string at runtime. It compiles source code in memory, loads the resulting bytecode using a custom class loader, and executes methods using reflection. This is useful for applications that need to execute dynamically generated or user-provided Java code without writing it to disk.
The following diagram illustrates the architecture of SourceRunner:
graph TD
subgraph "User Code"
A[Source Code as String]
end
subgraph "SourceRunner Library"
B[SourceRunner]
C[InMemoryCompiler]
D[InMemoryFileManager]
E[Custom ClassLoader]
F[InMemoryByteCode]
G[Reflection API]
end
subgraph "Java Compiler API"
H[javax.tools.JavaCompiler]
end
A --> B;
B --> C;
C --> H;
H --> D;
D --> F;
D --> E;
B --> E;
B --> G;
- Source Code Input: The user provides Java source code as a list of strings to the
SourceRunner
. - Compilation: The
SourceRunner
passes the source code to theInMemoryCompiler
. - Java Compiler: The
InMemoryCompiler
obtains an instance of the system'sJavaCompiler
and uses it to compile the source code. - In-Memory File Management: The
InMemoryFileManager
intercepts the output of the compiler. Instead of writing.class
files to disk, it stores the compiled bytecode inInMemoryByteCode
objects, which are held in a map in memory. - Custom Class Loading: The
InMemoryFileManager
uses a customClassLoader
to load classes directly from the in-memory bytecode. This allows the application to use the compiled classes without them ever existing on the file system. - Instantiation and Execution: The
SourceRunner
uses the custom class loader to load the desired class. It then uses the Java Reflection API to instantiate the class and invoke its methods.
SourceRunner can handle multiple source files with dependencies between them. When you provide a list of source code strings to the SourceRunner
, it compiles all of them. The custom class loader can then load any of the compiled classes, resolving dependencies between them from the in-memory bytecode store.
For example, if you have two classes, ClassA
and ClassB
, where ClassA
depends on ClassB
, you can provide both of their source codes to the SourceRunner
. The compiler will compile both, and when ClassA
is loaded, the custom class loader will find and load ClassB
from memory.
- In-Memory Compilation: Compiles Java source code from strings without creating
.java
files. - Dynamic Class Loading: Loads compiled classes on the fly.
- Method Execution: Execute methods on the dynamically loaded classes using reflection.
- No File I/O: The entire process from source to execution happens in memory.
- Simple API: A clean and easy-to-use interface.
- Java 21 or higher (as specified in
pom.xml
).
To use SourceRunner in your Maven project, add the following dependency to your pom.xml
:
<dependency>
<groupId>com.leng25.sourcerunner</groupId>
<artifactId>SourceRunner</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
Here is a simple example of how to use SourceRunner to compile and run a "Hello, World!" program.
import com.leng25.sourcerunner.SourceRunner;
import java.util.List;
public class Main {
public static void main(String[] args) {
// 1. Your Java code as a string
String sourceCode = """
package com.example;
public class Greeter {
public String greet(String name) {
return "Hello, " + name + "!";
}
}
""";
try {
// 2. Create a SourceRunner instance with your source code
SourceRunner runner = new SourceRunner(List.of(sourceCode));
// 3. Instantiate your class
Object greeterInstance = runner.instanciate("com.example.Greeter");
// 4. Run a method on the instance
String result = runner.run(greeterInstance, "greet", "World");
// 5. Print the result
System.out.println(result); // Outputs: Hello, World!
} catch (Exception e) {
e.printStackTrace();
}
}
}
Contributions are welcome! If you have ideas for improvements or find a bug, please open an issue or submit a pull request.
- Fork the repository.
- Create a new branch (
git checkout -b feature/YourFeature
). - Commit your changes (
git commit -m 'Add some feature'
). - Push to the branch (
git push origin feature/YourFeature
). - Open a pull request.
This project is licensed under the MIT License - see the LICENSE.md file for details.