docs/architecture/ExtensionStorageDesign.md
This document describes the design for BTrace extension storage and loading mechanism. The goal is to move extensions from the boot JAR to a dedicated extension system with built-in and user-configurable locations.
Currently, extensions (like btrace-metrics) are packaged into the boot JAR (btrace-boot.jar) along with core BTrace runtime classes. This approach has several limitations:
BTRACE_HOME/
├── bin/
│ ├── btrace
│ └── btracec
├── libs/
│ ├── btrace-agent.jar
│ ├── btrace-boot.jar # Core runtime only, no extensions
│ ├── btrace-client.jar
│ └── extensions/ # Built-in extensions directory
│ ├── btrace-metrics-2.3.0.jar
│ ├── btrace-statsd-2.3.0.jar
│ └── Readme.md
└── docs/
└── ...
Extensions are discovered in the following order (later locations override earlier):
BTRACE_HOME/extensions//etc/btrace/extensions/ (Unix) or %PROGRAMDATA%\btrace\extensions\ (Windows)~/.btrace/extensions/$BTRACE_EXT_PATH (colon-separated paths)--ext-path <path> (btrace command)./.btrace/extensions/ (relative to script location)btrace-metrics-2.3.0.jar
├── META-INF/
│ ├── MANIFEST.MF
│ ├── btrace-extension.properties # Extension metadata
│ └── services/
│ └── org.openjdk.btrace.core.extensions.Extension
├── org/openjdk/btrace/metrics/
│ ├── MetricsService.class
│ ├── histogram/
│ └── stats/
└── ... (shaded dependencies)
btrace-extension.properties)# Extension identity
extension.id=btrace-metrics
extension.version=2.3.0
extension.name=BTrace Metrics
extension.description=High-performance metrics with HdrHistogram
# API compatibility
btrace.api.version=2.3+
java.version=8+
# Service providers (optional, can also use META-INF/services)
services=org.openjdk.btrace.metrics.MetricsService
# Dependencies on other extensions (optional)
requires.extensions=
# Shadowed packages (for conflict detection)
shaded.packages=org.HdrHistogram->org.openjdk.btrace.metrics.shaded.hdrhistogram,\
com.clearspring.analytics->org.openjdk.btrace.metrics.shaded.clearspring
public class ExtensionLoader {
private final List<ExtensionRepository> repositories;
// Scan all configured locations
public List<ExtensionDescriptor> discoverExtensions() {
List<ExtensionDescriptor> extensions = new ArrayList<>();
for (ExtensionRepository repo : repositories) {
extensions.addAll(repo.scan());
}
// Resolve conflicts (latest version wins)
return resolveExtensions(extensions);
}
}
When the compiler encounters an @Injected service:
// In Compiler.java
private void loadRequiredExtensions(List<String> serviceTypes) {
for (String serviceType : serviceTypes) {
ExtensionDescriptor ext = extensionLoader.findExtensionForService(serviceType);
if (ext != null && !ext.isLoaded()) {
extensionLoader.load(ext);
}
}
}
Bootstrap ClassLoader
|
System ClassLoader
|
BTrace Boot ClassLoader (btrace-boot.jar)
|
+-- Extension ClassLoader 1 (btrace-metrics)
|
+-- Extension ClassLoader 2 (btrace-statsd)
|
+-- ...
|
BTrace Script ClassLoader (compiled script)
Each extension gets its own classloader for isolation, but they can see:
btrace.conf)# Extension directories (colon-separated on Unix, semicolon on Windows)
extension.path=${BTRACE_HOME}/extensions:${HOME}/.btrace/extensions
# Extension loading behavior
extension.lazy-load=true
extension.fail-on-missing=false
extension.conflict-resolution=latest-version
# Extension-specific settings
extension.btrace-metrics.enabled=true
extension.btrace-statsd.enabled=true
# Override extension path
export BTRACE_EXT_PATH="/opt/btrace-extensions:/usr/local/btrace/ext"
# Disable lazy loading (load all extensions at startup)
export BTRACE_EXT_LAZY_LOAD=false
# Specify additional extension directories
btrace --ext-path /custom/extensions PID script.java
# Disable built-in extensions
btrace --no-builtin-ext PID script.java
# Enable specific extensions only
btrace --ext btrace-metrics,btrace-statsd PID script.java
package com.example.myext;
import org.openjdk.btrace.core.extensions.Extension;
public class MyExtensionService extends Extension {
@Override
public void onStart(BTraceRuntime runtime) {
// Initialize extension
}
}
com.example.myext.MyExtensionService
META-INF/btrace-extension.properties:extension.id=my-extension
extension.version=1.0.0
btrace.api.version=2.3+
shadowJar {
// Shade dependencies to avoid conflicts
relocate 'com.external.lib', 'com.example.myext.shaded.lib'
}
cp my-extension-1.0.0.jar $BTRACE_HOME/extensions/
# or
cp my-extension-1.0.0.jar ~/.btrace/ext/
public class FileSystemExtensionRepository implements ExtensionRepository {
private final Path extensionDir;
@Override
public List<ExtensionDescriptor> scan() {
List<ExtensionDescriptor> extensions = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(extensionDir, "*.jar")) {
for (Path jar : stream) {
ExtensionDescriptor desc = parseExtension(jar);
if (desc != null) {
extensions.add(desc);
}
}
}
return extensions;
}
}
# In btrace.conf
extension.repository.url=https://extensions.btrace.io/repository
extension.repository.cache=${HOME}/.btrace/cache
extensions/ in distributionExtensionDescriptor classExtensionRepository interface and FileSystemRepositoryExtensionClassLoader with proper parent delegationTotal Estimated Effort: 9-15 days
Extension Verification:
Trusted Locations:
Classloading Isolation:
Resource Limits: