Version: 1.0.0 Last Updated: February 3, 2026
This document describes the architecture of Gade (Groovy Analytics Development Environment), covering the major components, runtime execution models, communication protocols, and extension points.
- System Overview
- Component Architecture
- Runtime Execution Models
- JSON-RPC Protocol
- Code Completion System
- Extension Points
- Build System
- Data Flow
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Gade Main Process β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β JavaFX UI Layer β β
β β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββββββ β β
β β β Editor β β Console β β Environmentβ β Connections β β β
β β β Tabs β β Output β β Variables β β Database β β β
β β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Application Logic β β
β β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββββββ β β
β β β Code β β Runtime β β Git β β File β β β
β β β Completionβ β Manager β β Integrationβ β Manager β β β
β β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Embedded Runtimes β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Gade Runtime (GroovyShell) - In-Process β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β JSON-RPC β JSON-RPC
β (Socket) β (Socket)
β β
βββββββββββββββββββββββββββ βββββββββββββββββββββββββββ
β Gradle Runtime Process β β Maven Runtime Process β
β (Separate JVM) β β (Separate JVM) β
β βββββββββββββββββββββ β β βββββββββββββββββββββ β
β β GradleRunner β β β β MavenRunner β β
β β Groovy Engine β β β β Groovy Engine β β
β β Gradle Tooling APIβ β β β Maven Embedder β β
β βββββββββββββββββββββ β β βββββββββββββββββββββ β
βββββββββββββββββββββββββββ βββββββββββββββββββββββββββ
- Process Isolation: Gradle and Maven runtimes execute in separate JVMs to prevent classpath conflicts
- Pluggable Architecture: Code completion, syntax highlighting, and runtimes are extensible
- Reactive UI: JavaFX Platform.runLater() ensures UI remains responsive during long operations
- Caching: Aggressive caching of classpaths, completions, and Git state for performance
- Single Responsibility: Each component has a focused purpose (see Sprint 3 refactors)
Package: se.alipsa.gade
- Application entry point
- Menu bar, toolbar, tabs, console
- Coordinates UI components
- EditorTab - Base class for all editor tabs
- GroovyTab - Groovy script editor
- SqlTab - SQL script editor
- JsTab - JavaScript editor
- MarkdownTab - Markdown viewer/editor
Each tab extends ExecutableTab which provides:
- Execute button management
- TaskListener implementation (disable during execution)
- Common file operations
See: docs/adr/002-executable-tab-pattern.md
- ConsoleTextArea - Output display with syntax coloring
- GroovyEngine - Embedded Groovy execution engine
- RuntimeProcessRunner - External runtime process manager
Refactored in Sprint 3 (Task #10): ConsoleComponent split into:
- ConsoleButtonPanel - Button management
- ConsoleEnvironmentPane - Variable display
- ConsoleTextArea - Output display
Package: se.alipsa.gade.code.completion
CompletionRegistry (Singleton, Thread-Safe)
- Central registry for all completion engines
- Language β Engine mapping
- Cache invalidation coordination
CompletionEngine (Interface)
complete(CompletionContext) β List<CompletionItem>supportedLanguages() β Set<String>invalidateCache()- Called when classpath changes
CompletionContext (Immutable)
- Full document text
- Caret position
- Utility methods:
isMemberAccess(),isInsideString(),isInsideComment()
CompletionItem (Immutable)
- Completion text, display text, kind (method, field, keyword, etc.)
- Sort priority, cursor offset
- Builder pattern for complex items
GroovyCompletionEngine
- Uses ClassGraph for fast class scanning
- Reflection for member access
- Caches scanned classes (invalidated on classpath change)
SqlCompletionEngine
- SQL keywords, functions
- Table/column names from connected databases
- JSqlParser for syntax analysis
JavaScriptCompletionEngine
- JavaScript keywords
- DOM API methods
- Node.js completions
See: docs/adr/003-completion-engine-architecture.md
Package: se.alipsa.gade.runtime
GadeRuntime (Embedded)
- In-process GroovyShell execution
- Fast, no IPC overhead
- No external dependency management
- Use case: Quick scripts, exploration
GradleRuntime (Subprocess)
- Separate JVM process
- Full Gradle Tooling API
- Dependency resolution via build.gradle
- Use case: Projects with dependencies
MavenRuntime (Subprocess)
- Separate JVM process
- maven-utils library for classpath resolution
- Dependency resolution via pom.xml
- Use case: Enterprise projects, POM inheritance
CustomRuntime (Subprocess)
- User-specified Java executable
- Custom classpath
- Use case: Testing against specific JDK versions
See: docs/adr/001-separate-process-runtimes.md
Manages external runtime processes:
Responsibilities:
- Start/stop runtime subprocess
- JSON-RPC message handling
- stdout/stderr forwarding to console
- Script evaluation coordination
- Graceful shutdown
Lifecycle:
- Start subprocess with
GadeRunnerMain - Wait for "hello" handshake
- Send "eval" requests with script + bindings
- Receive "result", "out", "err", "bindings" messages
- On completion, send "shutdown" or kill process
Thread Safety: All public methods are synchronized
Package: se.alipsa.gade.utils.gradle
Refactored in Sprint 3 (Task #10): GradleUtils split into:
- Gradle Tooling API connection
- Classpath resolution
- Dependency introspection
- Error detection with helpful hints
Key Methods:
resolveClasspath(File projectDir) β Set<File>getCacheDir() β FilehasGradleProject(File dir) β booleangetProjectFingerprint(File dir) β String
- Classpath fingerprinting (build.gradle + settings.gradle hash)
- JSON-based cache storage
- Cache invalidation on file changes
Cache Location: ~/.gade/gradle-cache/<project-hash>.json
- Parse Gradle errors
- Suggest fixes for common issues
- Example: "Could not find X" β "Add repository: mavenCentral()"
Thread Safety: All classes are thread-safe
Package: se.alipsa.gade.inout.git
Library: JGit (Eclipse)
Features:
- Clone, init, commit, push, pull
- Branch management
- Diff viewing
- Stash operations
- Tag management
Components:
- GitUtils - JGit wrapper methods
- GitStatusMonitor - Background thread monitoring .git directory
- GitDialog - UI dialogs for Git operations
Package: se.alipsa.gade.environment.connections
Connection Types:
- JDBC (standard databases)
- BigQuery (Google Cloud Platform)
Components:
- ConnectionHandler - JDBC connection management
- ConnectionInfo - Connection metadata storage
- ConnectionsTab - UI for managing connections
JDBC Driver Loading:
- Dynamic classloading via
data-utilslibrary - No system classloader pollution
- Add drivers via UI or Gradle dependencies
Tool: Gradle 9.3.1
Plugins:
application- Main class specificationgroovy- Groovy compilationorg.javamodularity.moduleplugin- Java modules (JPMS)org.beryx.runtime- Platform-specific distributionsorg.openjfx.javafxplugin- JavaFX dependenciesjacoco- Code coverageme.champeau.jmh- Performance benchmarks
Key Build Tasks:
./gradlew run # Development run
./gradlew build # Compile + test
./gradlew test # Run all tests
./gradlew jacocoTestReport # Generate coverage report
./gradlew jmh # Run benchmarks
./gradlew runtimeZip # Create platform-specific distributionsβββββββββββββββββββββββββββββββββββββββββββββββ
β Gade Main Process β
β β
β User Script β
β β β
β ββββββββββββββββββββββββββββββββββββββ β
β β GroovyShell.evaluate(script) β β
β β - Same classloader as Gade β β
β β - Access to all Gade classes β β
β β - stdout β ConsoleTextArea β β
β ββββββββββββββββββββββββββββββββββββββ β
β β β
β Result β Environment Tab β
βββββββββββββββββββββββββββββββββββββββββββββββ
Advantages:
- β‘ Fast (no IPC overhead)
- Simple (no subprocess management)
Limitations:
- β No external dependency management
- β Classpath shared with Gade (potential conflicts)
βββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
β Gade Main Process β β Gradle Runtime Process β
β β β β
β User Script β β GradleRunnerMain β
β β β β β
β ββββββββββββββββββββββ β Socket β ββββββββββββββββββββββ β
β β RuntimeProcessRunnerββββββΌβββββββββββ€βΊβ JSON-RPC Server β β
β β - Start process β β JSON β β - Receive "eval" β β
β β - Send "eval" β β RPC β β - Execute script β β
β β - Receive "result" β β β β - Send "result" β β
β ββββββββββββββββββββββ β β ββββββββββββββββββββββ β
β β β β β β
β Result β Environment Tab β β GroovyShell.evaluate() β
β β β (with Gradle classpath)β
βββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
Gradle Classpath Resolution:
- Read build.gradle
- Use Gradle Tooling API to resolve dependencies
- Cache resolved classpath (fingerprinted by build.gradle hash)
- Pass classpath to subprocess via command-line argument
Advantages:
- β Full Gradle support (dependencies, plugins, etc.)
- β Isolated classpath (no conflicts with Gade)
Limitations:
- β±οΈ Slower first run (dependency download)
- β±οΈ Subprocess startup overhead (~1-2 seconds)
Similar to Gradle, but uses:
pom.xmlinstead ofbuild.gradle- maven-utils library instead of Gradle Tooling API
- MavenRunnerMain subprocess
Version: 1.0 (as of Sprint 3, Task #13)
See: src/main/java/se/alipsa/gade/runtime/ProtocolVersion.java
Purpose: Subprocess signals readiness
{
"type": "hello",
"port": 12345,
"protocolVersion": "1.0"
}Gade Response: Stores port, marks runner as ready
Purpose: Execute a script
{
"cmd": "eval",
"id": "uuid-1234",
"script": "println 'hello'\n1 + 2",
"bindings": {
"customVar": "value"
}
}Fields:
id- Unique request ID (used to correlate response)script- Groovy code to executebindings- Variables to inject into script context
Purpose: Return script result
{
"type": "result",
"id": "uuid-1234",
"result": "3"
}Purpose: Report script execution error
{
"type": "error",
"id": "uuid-1234",
"error": "No such property: foo",
"stacktrace": "groovy.lang.MissingPropertyException: ...\n at ..."
}Purpose: Forward stdout/stderr
{
"type": "out",
"text": "hello\n"
}{
"type": "err",
"text": "Error: something failed\n"
}Purpose: Update environment variables after script execution
{
"type": "bindings",
"bindings": {
"x": "10",
"y": "[1, 2, 3]"
}
}Gade Response: Updates Environment tab with new variables
Purpose: Gracefully shut down subprocess
{
"cmd": "shutdown"
}Runner Response: Exits with code 0
Gade Runner (Subprocess)
β β
βββββββββββββββ start process βββββββββββΊβ
β ββ listen on random port
β β
ββββββββ hello (port, version) βββββββββββ€
β β
βββββββ eval (id, script, bindings) βββββΊβ
β ββ execute script
ββββββββββββββ out ("hello") βββββββββββββ€
ββββββββββββββ result (id, "3") ββββββββββ€
ββββββββββββββ bindings ββββββββββββββββββ€
β β
βββββββ eval (id2, script2, ...) ββββββββΊβ
ββββββββββββββ error (id2, ...) ββββββββββ€
β β
βββββββββββββ shutdown βββββββββββββββββββΊβ
β ββ exit(0)
Connection Failures:
- Gade waits 10 seconds for "hello" handshake
- If timeout, kills process and shows error
Script Errors:
- Runner catches all exceptions
- Sends "error" message with stacktrace
- Does NOT crash subprocess (can run next script)
Runner Crash:
- Gade detects process exit
- Shows "Runtime process terminated unexpectedly"
- User can restart runtime
User Types β TextArea Event β CompletionPopup
β
CompletionRegistry.getCompletions()
β
βββββββββββββββ΄ββββββββββββββ
β β
GroovyCompletionEngine SqlCompletionEngine
β β
βββββββββββββββ΄βββββββββββ ββββββββββ΄βββββββββ
β β β β
ClassGraph Scan Reflection SQL Keywords DB Metadata
(all classes) (members) (tables, columns)
β β β β
ββββββββββββββ¬ββββββββββββ ββββββββββ¬βββββββββ
β β
List<CompletionItem> List<CompletionItem>
ββββββββββββββ¬ββββββββββββββββ
β
Sort by Priority
β
Display in Popup
1. ClassGraph Caching
// Scan once, cache forever (until invalidateCache())
ClassGraph classGraph = new ClassGraph()
.enableClassInfo()
.enableMethodInfo()
.scan();
cache.put("classGraph", classGraph);2. Completion Item Pre-computation
// Convert classes to CompletionItems eagerly
List<CompletionItem> classCompletions =
scanResult.getAllClasses()
.stream()
.map(cls -> CompletionItem.class(cls.getName()))
.toList();3. Prefix Filtering
// Filter by prefix on retrieval (not during scan)
String prefix = context.tokenPrefix();
return cachedItems.stream()
.filter(item -> item.label().startsWith(prefix))
.toList();4. Lazy Loading
// Only scan when completion is actually requested
if (!context.isMemberAccess() && !userPressedCtrlSpace) {
return Collections.emptyList(); // Don't scan yet
}Target: < 100ms completion time (benchmarked in Task #27)
To add a new language completion:
- Implement CompletionEngine:
public class MyLangCompletionEngine implements CompletionEngine {
@Override
public List<CompletionItem> complete(CompletionContext context) {
// Your completion logic
}
@Override
public Set<String> supportedLanguages() {
return Set.of("mylang");
}
}- Register on startup:
// In Gade.java or extension loader
CompletionRegistry.getInstance().register(new MyLangCompletionEngine());- Handle cache invalidation:
@Override
public void invalidateCache() {
myCache.clear();
}See: Javadoc for CompletionEngine, CompletionItem, CompletionContext
Interface: CompletionEngine
Examples:
- GroovyCompletionEngine
- SqlCompletionEngine
- JavaScriptCompletionEngine
Hook: CompletionRegistry.register(engine)
Library: RichTextFX
Current Implementations:
- Groovy (keywords, strings, comments, numbers)
- SQL (keywords, identifiers, operators)
- JavaScript (ES6 keywords)
- XML, HTML, Markdown
Extension: Add language-specific regex patterns in CodeTextArea.java
Interface: RuntimeConfiguration
Steps:
- Implement subprocess main class (like
GradleRunnerMain) - Implement JSON-RPC protocol
- Add UI for runtime selection
- Register with RuntimeManager
Interface: Standard JDBC
Current Support:
- PostgreSQL, MySQL, H2, SQL Server, Oracle
- BigQuery (custom connector)
Extension: Implement JDBC driver or custom ConnectionHandler
Pattern: Tab factories in FileOpener.java
Example:
if (file.getName().endsWith(".gmd")) {
return new MarkdownTab(gui, file);
} else if (file.getName().endsWith(".sql")) {
return new SqlTab(gui, file);
}Extension: Add new tab type for your file extension
module se.alipsa.gade {
// JavaFX
requires javafx.controls;
requires javafx.web;
// Groovy
requires org.apache.groovy;
requires org.apache.groovy.sql;
// Build tools
requires org.gradle.tooling;
requires se.alipsa.maven.utils;
// UI libraries
requires org.fxmisc.richtext;
requires org.fxmisc.flowless;
// Utilities
requires io.github.classgraph;
requires org.eclipse.jgit;
requires com.fasterxml.jackson.databind;
// Database
requires java.sql;
requires se.alipsa.groovy.datautil;
// Matrix (data science)
requires se.alipsa.groovy.matrix;
requires se.alipsa.groovy.charts;
// Exports (for extensions)
exports se.alipsa.gade.code.completion;
exports se.alipsa.gade.runtime;
}
Tool: org.beryx.runtime plugin (jlink)
Process:
- Compile Java β .class files
- Create modular JAR
- jlink creates minimal JRE (Java 21 + JavaFX)
- Bundle application + JRE β platform-specific package
Outputs:
gade-linux.zip(x64)gade-macos.zip(x64 + ARM64)gade-windows.zip(x64)
Advantages:
- No JDK installation required
- Smaller download (JRE includes only used modules)
- Consistent Java version across platforms
User clicks "Run"
β
ExecutableTab.executeAction()
β
ConsoleComponent.execute(script)
β
GroovyEngine.eval(script)
β
GroovyShell.evaluate(script)
β
Result returned
β
ConsoleComponent.displayResult()
β
Environment tab updated (variables)
User clicks "Run"
β
ExecutableTab.executeAction()
β
ConsoleComponent.execute(script)
β
RuntimeProcessRunner.execute(script)
β
Send JSON: {"cmd":"eval","id":"...","script":"..."}
β
[Process boundary - Socket IPC]
β
GradleRunnerMain receives message
β
GroovyShell.evaluate(script) [with Gradle classpath]
β
stdout β Send JSON: {"type":"out","text":"..."}
β
result β Send JSON: {"type":"result","id":"...","result":"..."}
β
[Process boundary - Socket IPC]
β
RuntimeProcessRunner.handleMessage()
β
ConsoleComponent.displayResult()
β
Environment tab updated
User types "text."
β
TextArea KeyTyped event
β
CompletionPopup.show()
β
CompletionRegistry.getCompletions(context)
β
For each registered engine:
engine.complete(context)
β
Engines return List<CompletionItem>
β
Merge + sort by priority
β
Display top 10 in popup
β
User presses Enter
β
Insert item.insertText()
β
Move cursor by item.cursorOffset()
User opens project with build.gradle
β
GradleUtils.resolveClasspath(projectDir)
β
Calculate fingerprint (hash of build.gradle)
β
Check cache: ~/.gade/gradle-cache/<fingerprint>.json
β
Cache miss β Use Gradle Tooling API
β
GradleConnector.forProjectDirectory(dir)
β
connection.model(IdeaProject.class).get()
β
Extract dependencies from model
β
Convert to Set<File> (JAR paths)
β
Save to cache (JSON)
β
Return classpath
β
Pass to RuntimeProcessRunner as -cp argument
Gade Runtime:
β οΈ Runs in same process as UIβ οΈ Full access to Gade internalsβ οΈ Can callSystem.exit(), modify files, etc.
Gradle/Maven Runtime:
- β Separate process (limited blast radius)
β οΈ Still has full filesystem/network accessβ οΈ No sandboxing (Groovy Security Manager deprecated)
Recommendation: Only run trusted scripts. Gade is not designed for untrusted code execution.
- Passwords stored in plaintext in connection configuration
- No encryption at rest
β οΈ Ensure.gade/connections.jsonhas restricted permissions
- SSH keys managed by OS (not Gade)
- HTTPS credentials via Git credential manager
- β No password storage in Gade
Measured on 2023 MacBook Pro (M2):
- Cold start: ~3-4 seconds
- Warm start: ~2 seconds
Bottlenecks:
- JavaFX initialization (1-1.5s)
- Module loading (0.5s)
- UI construction (0.5s)
Target: < 100ms (per roadmap Task #27)
Actual (benchmarked):
- Simple string completion: ~42ms
- Complex import completion: ~68ms
- Member access: ~51ms
See: docs/improvements/task-27-performance-benchmarks.md
First resolution (no cache):
- Small project (~10 deps): 5-10 seconds
- Medium project (~50 deps): 15-30 seconds
- Large project (~100+ deps): 30-60 seconds
Cached resolution:
- < 100ms (read JSON from disk)
Typical usage:
- Minimum: 200MB (empty project)
- Normal: 500MB-1GB (with Gradle runtime)
- Heavy: 2-4GB (large datasets in matrix)
Recommendation: -Xmx8G for data science workloads
-
Language Server Protocol (LSP)
- Replace custom completion with LSP
- Benefit: Reuse existing language servers
- Challenge: Integration with JavaFX UI
-
Plugin System
- Dynamic plugin loading
- Separate classloader per plugin
- Extension registry
-
Distributed Execution
- Remote script execution (SSH)
- Cluster support (Spark, etc.)
-
Scripting API
- Programmatic access to Gade features
- Automate workflows via scripts
-
Web-Based UI
- Browser-based alternative to JavaFX
- Better remote access
- Challenge: Requires complete rewrite
- ADR 001: Separate Process Runtimes
- ADR 002: GroovyShell vs JSR223
- ADR 003: Completion Engine Architecture
- User Guide
- API Documentation
Document Version: 1.0.0 Last Updated: February 3, 2026 Maintained By: Gade Development Team