GraalVM Polyglot API for multi-language runtime environments with host-guest interoperability and security controls.
—
GraalVM Polyglot API provides comprehensive monitoring and resource management capabilities, including execution event tracking, resource consumption limits, exception handling, and performance profiling for polyglot applications.
The Management class provides the foundation for monitoring polyglot execution through event listeners and profiling tools.
public final class Management {
/**
* Creates a new Management.Builder for configuration.
* @return new builder instance
*/
public static Management.Builder newBuilder();
/**
* Creates an execution listener for monitoring code execution.
* @return new ExecutionListener
*/
public ExecutionListener newExecutionListener();
}public static final class Management.Builder {
/**
* Builds the Management instance.
* @return configured Management
*/
public Management build();
}Management Setup Example:
import org.graalvm.polyglot.management.Management;
import org.graalvm.polyglot.management.ExecutionListener;
// Create management system
Management management = Management.newBuilder().build();
// Create execution listener
ExecutionListener listener = management.newExecutionListener();
// Configure monitoring...ExecutionListener provides fine-grained monitoring of code execution, allowing you to track entry/exit events, performance metrics, and execution context.
public final class ExecutionListener implements AutoCloseable {
/**
* Sets callback for execution entry events.
* @param listener callback to invoke on entry
*/
public void onEnter(Consumer<ExecutionEvent> listener);
/**
* Sets callback for execution return events.
* @param listener callback to invoke on return
*/
public void onReturn(Consumer<ExecutionEvent> listener);
/**
* Attaches listener to an engine for monitoring all contexts.
* @param engine the engine to monitor
*/
public void attach(Engine engine);
/**
* Attaches listener to a specific context.
* @param context the context to monitor
*/
public void attach(Context context);
/**
* Checks if the listener is attached to any engine or context.
* @return true if attached
*/
public boolean isAttached();
/**
* Checks if the listener has been closed.
* @return true if closed
*/
public boolean isClosed();
/**
* Closes the listener and detaches from all engines/contexts.
*/
@Override
public void close();
}public final class ExecutionEvent {
/**
* Gets the root function/method name being executed.
* @return root name or null if not available
*/
public String getRootName();
/**
* Gets the source location of the execution point.
* @return source location or null if not available
*/
public SourceSection getLocation();
/**
* Checks if this event represents a statement execution.
* @return true if statement
*/
public boolean isStatement();
/**
* Checks if this event represents an expression evaluation.
* @return true if expression
*/
public boolean isExpression();
/**
* Checks if this event represents root-level execution.
* @return true if root
*/
public boolean isRoot();
/**
* Gets input values for the execution (on entry).
* @return array of input values
*/
public Value[] getInputValues();
/**
* Gets the return value (on return events).
* @return return value or null
*/
public Value getReturnValue();
/**
* Gets exception if execution failed (on return events).
* @return exception or null if no exception
*/
public RuntimeException getException();
}Comprehensive Execution Monitoring Example:
import org.graalvm.polyglot.management.*;
import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
public class DetailedExecutionMonitor {
private final Map<String, ExecutionStats> functionStats = new ConcurrentHashMap<>();
private final AtomicLong totalExecutions = new AtomicLong(0);
private final AtomicLong totalExecutionTime = new AtomicLong(0);
public void setupMonitoring(Context context) {
Management management = Management.newBuilder().build();
ExecutionListener listener = management.newExecutionListener();
// Track execution entry
listener.onEnter(this::onExecutionEnter);
// Track execution exit
listener.onReturn(this::onExecutionReturn);
// Attach to context
listener.attach(context);
// Setup automatic cleanup
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
listener.close();
printStatistics();
}));
}
private void onExecutionEnter(ExecutionEvent event) {
String rootName = event.getRootName();
if (rootName != null) {
ExecutionStats stats = functionStats.computeIfAbsent(rootName,
k -> new ExecutionStats(k));
stats.enterExecution();
totalExecutions.incrementAndGet();
// Log entry with details
SourceSection location = event.getLocation();
if (location != null) {
System.out.printf("ENTER: %s at %s:%d%n",
rootName,
location.getSource().getName(),
location.getStartLine());
}
// Log input values
Value[] inputs = event.getInputValues();
if (inputs.length > 0) {
System.out.print(" Inputs: ");
for (int i = 0; i < inputs.length; i++) {
if (i > 0) System.out.print(", ");
System.out.print(inputs[i].toString());
}
System.out.println();
}
}
}
private void onExecutionReturn(ExecutionEvent event) {
String rootName = event.getRootName();
if (rootName != null) {
ExecutionStats stats = functionStats.get(rootName);
if (stats != null) {
long duration = stats.exitExecution();
totalExecutionTime.addAndGet(duration);
// Log return value or exception
RuntimeException exception = event.getException();
if (exception != null) {
System.out.printf("EXIT: %s (EXCEPTION: %s)%n",
rootName, exception.getMessage());
stats.recordException(exception);
} else {
Value returnValue = event.getReturnValue();
System.out.printf("EXIT: %s -> %s (took %d ns)%n",
rootName,
returnValue != null ? returnValue.toString() : "void",
duration);
}
}
}
}
public void printStatistics() {
System.out.println("\n=== Execution Statistics ===");
System.out.printf("Total executions: %d%n", totalExecutions.get());
System.out.printf("Total time: %.2f ms%n", totalExecutionTime.get() / 1_000_000.0);
functionStats.values().stream()
.sorted((a, b) -> Long.compare(b.getTotalTime(), a.getTotalTime()))
.forEach(stats -> {
System.out.printf("Function: %s%n", stats.getFunctionName());
System.out.printf(" Calls: %d%n", stats.getCallCount());
System.out.printf(" Total time: %.2f ms%n", stats.getTotalTime() / 1_000_000.0);
System.out.printf(" Average time: %.2f ms%n", stats.getAverageTime() / 1_000_000.0);
System.out.printf(" Exceptions: %d%n", stats.getExceptionCount());
});
}
private static class ExecutionStats {
private final String functionName;
private final AtomicLong callCount = new AtomicLong(0);
private final AtomicLong totalTime = new AtomicLong(0);
private final AtomicLong exceptionCount = new AtomicLong(0);
private volatile long enterTime;
public ExecutionStats(String functionName) {
this.functionName = functionName;
}
public void enterExecution() {
callCount.incrementAndGet();
enterTime = System.nanoTime();
}
public long exitExecution() {
long duration = System.nanoTime() - enterTime;
totalTime.addAndGet(duration);
return duration;
}
public void recordException(RuntimeException exception) {
exceptionCount.incrementAndGet();
}
// Getters...
public String getFunctionName() { return functionName; }
public long getCallCount() { return callCount.get(); }
public long getTotalTime() { return totalTime.get(); }
public long getAverageTime() {
long count = callCount.get();
return count > 0 ? totalTime.get() / count : 0;
}
public long getExceptionCount() { return exceptionCount.get(); }
}
}
// Usage
Context context = Context.create("js");
DetailedExecutionMonitor monitor = new DetailedExecutionMonitor();
monitor.setupMonitoring(context);
// Execute monitored code
context.eval("js", """
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
function calculate() {
let result = 0;
for (let i = 0; i < 10; i++) {
result += fibonacci(i);
}
return result;
}
calculate();
""");ResourceLimits provides mechanisms to control and limit resource consumption during polyglot execution.
public final class ResourceLimits {
/**
* Creates a new ResourceLimits.Builder.
* @return new builder instance
*/
public static ResourceLimits.Builder newBuilder();
}public static final class ResourceLimits.Builder {
/**
* Sets statement execution limit.
* @param limit maximum number of statements to execute
* @param sourceFilter predicate to filter which sources are counted (null = all sources)
* @return this builder
*/
public ResourceLimits.Builder statementLimit(long limit, Predicate<Source> sourceFilter);
/**
* Sets callback for when resource limits are exceeded.
* @param onLimit callback to invoke on limit exceeded
* @return this builder
*/
public ResourceLimits.Builder onLimit(Consumer<ResourceLimitEvent> onLimit);
/**
* Builds the ResourceLimits.
* @return configured ResourceLimits
*/
public ResourceLimits build();
}public final class ResourceLimitEvent {
/**
* Gets the type of limit that was exceeded.
* @return limit type identifier
*/
public String getLimitType();
/**
* Gets the amount of resource consumed.
* @return consumed amount
*/
public long getConsumed();
/**
* Gets the configured limit value.
* @return limit value
*/
public long getLimit();
}Resource Limits Example:
import org.graalvm.polyglot.ResourceLimits;
import org.graalvm.polyglot.ResourceLimitEvent;
public class ResourceControlledExecution {
public static void executeWithLimits() {
// Configure resource limits
ResourceLimits limits = ResourceLimits.newBuilder()
.statementLimit(50000, source -> !source.isInternal()) // Only count user code
.onLimit(event -> {
System.err.printf("Resource limit exceeded: %s%n", event.getLimitType());
System.err.printf("Consumed: %d, Limit: %d%n",
event.getConsumed(), event.getLimit());
// Log for security audit
SecurityAuditLog.logResourceExhaustion(event);
// Could throw exception to terminate execution
throw new SecurityException("Statement limit exceeded: " + event.getConsumed());
})
.build();
// Create context with limits
Context context = Context.newBuilder("js")
.resourceLimits(limits)
.build();
try {
// This will trigger the limit
context.eval("js", """
let count = 0;
while (true) { // Infinite loop
count++;
if (count % 10000 === 0) {
console.log('Count:', count);
}
}
""");
} catch (PolyglotException e) {
if (e.isResourceExhausted()) {
System.err.println("Execution stopped due to resource exhaustion");
} else {
System.err.println("Other execution error: " + e.getMessage());
}
}
}
// Advanced resource management with custom limits
public static void advancedResourceControl() {
AtomicLong memoryUsage = new AtomicLong(0);
AtomicLong executionTime = new AtomicLong(System.nanoTime());
ResourceLimits limits = ResourceLimits.newBuilder()
.statementLimit(100000, null)
.onLimit(event -> {
// Check multiple resource types
long currentTime = System.nanoTime();
long elapsed = currentTime - executionTime.get();
if (elapsed > TimeUnit.SECONDS.toNanos(30)) { // 30 second timeout
throw new SecurityException("Execution timeout exceeded");
}
// Memory check (if available)
long currentMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
if (currentMemory > 100 * 1024 * 1024) { // 100MB limit
throw new SecurityException("Memory limit exceeded");
}
// Default action for statement limit
if ("StatementLimit".equals(event.getLimitType())) {
throw new SecurityException("Statement execution limit exceeded");
}
})
.build();
Context context = Context.newBuilder("js")
.resourceLimits(limits)
.build();
// Reset execution timer
executionTime.set(System.nanoTime());
try {
context.eval("js", userProvidedCode);
} catch (PolyglotException e) {
handleResourceExhaustion(e);
}
}
}
class SecurityAuditLog {
public static void logResourceExhaustion(ResourceLimitEvent event) {
System.err.printf("[SECURITY] Resource exhaustion - Type: %s, Consumed: %d, Limit: %d, Time: %s%n",
event.getLimitType(), event.getConsumed(), event.getLimit(), Instant.now());
}
}PolyglotException provides comprehensive error information for debugging and monitoring polyglot execution.
public final class PolyglotException extends RuntimeException {
// Exception type classification
public boolean isHostException();
public boolean isGuestException();
public boolean isSyntaxError();
public boolean isIncompleteSource();
public boolean isCancelled();
public boolean isExit();
public boolean isInterrupted();
public boolean isInternalError();
public boolean isResourceExhausted();
// Exception details
public Value getGuestObject();
public SourceSection getSourceLocation();
public int getExitStatus();
public Throwable asHostException();
// Stack trace operations
public Iterable<StackFrame> getPolyglotStackTrace();
public void printStackTrace(PrintWriter s);
public void printStackTrace(PrintStream s);
}public static final class PolyglotException.StackFrame {
/**
* Gets the source location of this stack frame.
* @return source location or null
*/
public SourceSection getSourceLocation();
/**
* Gets the root name (function/method name).
* @return root name or null
*/
public String getRootName();
/**
* Checks if this is a host (Java) frame.
* @return true if host frame
*/
public boolean isHostFrame();
/**
* Checks if this is a guest language frame.
* @return true if guest frame
*/
public boolean isGuestFrame();
/**
* Gets the host method name (if host frame).
* @return method name or null
*/
public String toHostFrame();
}Comprehensive Exception Handling Example:
public class PolyglotExceptionAnalyzer {
public static void analyzeException(PolyglotException exception) {
System.out.println("=== Polyglot Exception Analysis ===");
// Classify exception type
if (exception.isHostException()) {
System.out.println("Type: Host Exception (Java)");
Throwable hostException = exception.asHostException();
System.out.println("Host Exception: " + hostException.getClass().getSimpleName());
System.out.println("Message: " + hostException.getMessage());
} else if (exception.isGuestException()) {
System.out.println("Type: Guest Exception");
Value guestObject = exception.getGuestObject();
if (guestObject != null) {
System.out.println("Guest Exception Object: " + guestObject.toString());
// Try to extract common exception properties
if (guestObject.hasMembers()) {
if (guestObject.hasMember("name")) {
System.out.println("Exception Name: " + guestObject.getMember("name").asString());
}
if (guestObject.hasMember("message")) {
System.out.println("Exception Message: " + guestObject.getMember("message").asString());
}
}
}
} else if (exception.isSyntaxError()) {
System.out.println("Type: Syntax Error");
} else if (exception.isResourceExhausted()) {
System.out.println("Type: Resource Exhausted");
} else if (exception.isInterrupted()) {
System.out.println("Type: Execution Interrupted");
} else if (exception.isCancelled()) {
System.out.println("Type: Execution Cancelled");
} else if (exception.isExit()) {
System.out.println("Type: Exit");
System.out.println("Exit Status: " + exception.getExitStatus());
}
// Source location information
SourceSection location = exception.getSourceLocation();
if (location != null) {
System.out.printf("Location: %s:%d:%d%n",
location.getSource().getName(),
location.getStartLine(),
location.getStartColumn());
if (location.isAvailable()) {
System.out.println("Source Code: " + location.getCharacters());
}
}
// Stack trace analysis
System.out.println("\n=== Stack Trace ===");
int frameIndex = 0;
for (PolyglotException.StackFrame frame : exception.getPolyglotStackTrace()) {
System.out.printf("#%d: ", frameIndex++);
if (frame.isHostFrame()) {
System.out.printf("[HOST] %s%n", frame.toHostFrame());
} else if (frame.isGuestFrame()) {
System.out.printf("[GUEST] %s", frame.getRootName() != null ? frame.getRootName() : "<anonymous>");
SourceSection frameLocation = frame.getSourceLocation();
if (frameLocation != null) {
System.out.printf(" at %s:%d%n",
frameLocation.getSource().getName(),
frameLocation.getStartLine());
} else {
System.out.println();
}
}
}
}
public static void executeWithDetailedErrorHandling(Context context, String code) {
try {
Value result = context.eval("js", code);
System.out.println("Execution successful: " + result);
} catch (PolyglotException e) {
analyzeException(e);
// Log for debugging
System.err.println("\n=== Full Stack Trace ===");
e.printStackTrace();
// Take appropriate action based on exception type
if (e.isResourceExhausted()) {
// Resource exhaustion - security concern
SecurityLogger.logResourceExhaustion(e);
} else if (e.isSyntaxError()) {
// Syntax error - user input issue
System.err.println("Please check your code syntax");
} else if (e.isHostException()) {
// Host exception - could be programming error
Throwable cause = e.asHostException();
if (cause instanceof SecurityException) {
SecurityLogger.logSecurityViolation(e);
}
} else if (e.isInterrupted() || e.isCancelled()) {
// Execution control - expected behavior
System.out.println("Execution was controlled/cancelled");
}
}
}
}
// Usage with monitoring
public class MonitoredExecution {
public static void main(String[] args) {
// Setup comprehensive monitoring
Management management = Management.newBuilder().build();
ExecutionListener listener = management.newExecutionListener();
// Resource limits
ResourceLimits limits = ResourceLimits.newBuilder()
.statementLimit(10000, null)
.onLimit(event -> {
System.err.printf("Resource limit hit: %s (%d/%d)%n",
event.getLimitType(), event.getConsumed(), event.getLimit());
})
.build();
// Create monitored context
Context context = Context.newBuilder("js")
.resourceLimits(limits)
.build();
// Attach listener
listener.attach(context);
// Performance monitoring
listener.onEnter(event -> {
if (event.isRoot()) {
System.out.println("Starting execution of: " + event.getRootName());
}
});
listener.onReturn(event -> {
if (event.isRoot()) {
RuntimeException exception = event.getException();
if (exception != null) {
if (exception instanceof PolyglotException pe) {
PolyglotExceptionAnalyzer.analyzeException(pe);
}
} else {
Value returnValue = event.getReturnValue();
System.out.println("Execution completed: " +
(returnValue != null ? returnValue.toString() : "void"));
}
}
});
// Execute code with monitoring
String userCode = """
function complexCalculation(n) {
if (n <= 0) throw new Error('Invalid input');
let result = 1;
for (let i = 1; i <= n; i++) {
result *= i;
}
return result;
}
try {
complexCalculation(10);
} catch (e) {
console.error('Calculation failed:', e.message);
throw e;
}
""";
PolyglotExceptionAnalyzer.executeWithDetailedErrorHandling(context, userCode);
// Cleanup
listener.close();
context.close();
}
}
class SecurityLogger {
public static void logResourceExhaustion(PolyglotException e) {
System.err.println("[SECURITY] Resource exhaustion detected: " + e.getMessage());
}
public static void logSecurityViolation(PolyglotException e) {
System.err.println("[SECURITY] Security violation: " + e.getMessage());
}
}Combining execution listeners with resource monitoring provides comprehensive performance profiling capabilities.
public class PolyglotProfiler {
private final Map<String, PerformanceMetrics> metrics = new ConcurrentHashMap<>();
private final AtomicLong globalStartTime = new AtomicLong();
public void startProfiling(Context context) {
Management management = Management.newBuilder().build();
ExecutionListener listener = management.newExecutionListener();
globalStartTime.set(System.nanoTime());
listener.onEnter(this::recordEntry);
listener.onReturn(this::recordExit);
listener.attach(context);
// Setup periodic reporting
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(this::reportMetrics, 5, 5, TimeUnit.SECONDS);
}
private void recordEntry(ExecutionEvent event) {
String key = getEventKey(event);
if (key != null) {
PerformanceMetrics perf = metrics.computeIfAbsent(key,
k -> new PerformanceMetrics(k));
perf.recordEntry();
}
}
private void recordExit(ExecutionEvent event) {
String key = getEventKey(event);
if (key != null) {
PerformanceMetrics perf = metrics.get(key);
if (perf != null) {
perf.recordExit(event.getException() != null);
}
}
}
private String getEventKey(ExecutionEvent event) {
if (event.isRoot() && event.getRootName() != null) {
return event.getRootName();
}
return null;
}
public void reportMetrics() {
long totalTime = System.nanoTime() - globalStartTime.get();
System.out.printf("\n=== Performance Report (Total: %.2f ms) ===%n",
totalTime / 1_000_000.0);
metrics.values().stream()
.sorted((a, b) -> Long.compare(b.getTotalTime(), a.getTotalTime()))
.limit(10) // Top 10 functions
.forEach(this::printMetrics);
}
private void printMetrics(PerformanceMetrics metrics) {
System.out.printf("%-20s | Calls: %5d | Total: %8.2f ms | Avg: %6.2f ms | Errors: %3d%n",
metrics.getName(),
metrics.getCallCount(),
metrics.getTotalTime() / 1_000_000.0,
metrics.getAverageTime() / 1_000_000.0,
metrics.getErrorCount());
}
}
class PerformanceMetrics {
private final String name;
private final AtomicLong callCount = new AtomicLong(0);
private final AtomicLong totalTime = new AtomicLong(0);
private final AtomicLong errorCount = new AtomicLong(0);
private volatile long entryTime;
public PerformanceMetrics(String name) {
this.name = name;
}
public void recordEntry() {
callCount.incrementAndGet();
entryTime = System.nanoTime();
}
public void recordExit(boolean hasError) {
long duration = System.nanoTime() - entryTime;
totalTime.addAndGet(duration);
if (hasError) {
errorCount.incrementAndGet();
}
}
public String getName() { return name; }
public long getCallCount() { return callCount.get(); }
public long getTotalTime() { return totalTime.get(); }
public long getErrorCount() { return errorCount.get(); }
public long getAverageTime() {
long count = callCount.get();
return count > 0 ? totalTime.get() / count : 0;
}
}// Monitor only user code, not internal operations
ExecutionListener listener = management.newExecutionListener();
listener.onEnter(event -> {
SourceSection location = event.getSourceLocation();
if (location != null && !location.getSource().isInternal()) {
// Process only user code events
monitorUserExecution(event);
}
});// Use async logging to minimize performance impact
ExecutorService logExecutor = Executors.newSingleThreadExecutor();
listener.onReturn(event -> {
// Capture data quickly
ExecutionEventData data = new ExecutionEventData(event);
// Process asynchronously
logExecutor.submit(() -> processEventData(data));
});// Sample high-frequency events to reduce overhead
AtomicLong eventCounter = new AtomicLong(0);
listener.onEnter(event -> {
long count = eventCounter.incrementAndGet();
if (count % 100 == 0) { // Sample every 100th event
recordSampledEvent(event);
}
});Install with Tessl CLI
npx tessl i tessl/maven-org-graalvm-polyglot--graalvm-sdk