Architecture
Kopi's internal architecture and core components.
Overview
Kopi is a Rust-based JDK version manager that intercepts Java commands and automatically manages JDK versions:
graph TB
User["User runs:<br/>java MyApp.java"]
Shims["Kopi Shims<br/>(Command Interception)"]
Resolver["Version Resolver<br/>(Find Required JDK)"]
JDK["Correct JDK<br/>(~/.kopi/jdks/)"]
User --> Shims
Shims --> Resolver
Resolver --> JDK
JDK --> User
VersionFiles[".kopi-version<br/>.java-version"]
Metadata["JDK Metadata<br/>(Available Versions)"]
Config["Configuration<br/>(config.toml)"]
Resolver -.-> VersionFiles
Resolver -.-> Config
Shims -.-> Metadata
style Shims fill:#f9f,stroke:#333,stroke-width:2px
style Resolver fill:#bbf,stroke:#333,stroke-width:2px
Key components:
- Shims: Intercept Java commands and route to correct JDK
- Version Resolver: Determines which JDK version to use
- Metadata System: Tracks available JDK distributions and versions
- Storage Manager: Manages installed JDKs in
~/.kopi/jdks/
Core Components
1. Shim System (src/shim/
)
Shims are Rust binaries that intercept Java tool invocations:
flowchart LR
User["User executes:<br/>java MyApp.java"]
Shim["Shim Binary<br/>(~/.kopi/shims/java)"]
Validate["Security Validation<br/>(tool name & path)"]
Resolve["Version Resolution<br/>(find required JDK)"]
AutoInstall{"JDK<br/>Installed?"}
Prompt["Auto-install<br/>Prompt"]
Execute["Execute Tool<br/>(JDK/bin/java)"]
Output["Output to User"]
User --> Shim
Shim --> Validate
Validate --> Resolve
Resolve --> AutoInstall
AutoInstall -->|No| Prompt
AutoInstall -->|Yes| Execute
Prompt -->|Approved| Execute
Execute --> Output
style Shim fill:#f9f,stroke:#333,stroke-width:2px
style AutoInstall fill:#ffd,stroke:#333,stroke-width:2px
Key features:
- Security validation for tool names and paths
- Auto-installation capability with user prompts
- Platform-specific executable resolution
- Tool discovery and validation
- Minimal overhead through Rust implementation
2. Version Resolution (src/version/resolver.rs
)
Hierarchical version resolution with distribution support:
flowchart TD
Start["Version Resolution"]
EnvVar{"KOPI_JAVA_VERSION<br/>set?"}
KopiFile{".kopi-version<br/>exists?"}
JavaFile{".java-version<br/>exists?"}
Parent{"Parent dir<br/>has version?"}
Global{"Global default<br/>~/.kopi/version?"}
Error["Error: No version"]
Resolved["Version Resolved"]
Start --> EnvVar
EnvVar -->|Yes| Resolved
EnvVar -->|No| KopiFile
KopiFile -->|Yes| Resolved
KopiFile -->|No| JavaFile
JavaFile -->|Yes| Resolved
JavaFile -->|No| Parent
Parent -->|Yes| Resolved
Parent -->|No| Global
Global -->|Yes| Resolved
Global -->|No| Error
style EnvVar fill:#ffd,stroke:#333,stroke-width:2px
style Resolved fill:#dfd,stroke:#333,stroke-width:2px
style Error fill:#fdd,stroke:#333,stroke-width:2px
Version format support:
- Simple version:
21
or17.0.9
- Distribution-specific:
temurin@21
orcorretto@17
- Semantic matching:
17
matches17.0.15
- File formats:
.kopi-version
(native) and.java-version
(compatibility)
3. Metadata System (src/metadata/
)
Multi-source metadata provider with intelligent fallback:
flowchart TD
Request["Metadata Request"]
HTTP{"HTTP Source<br/>Available?"}
Foojay{"Foojay API<br/>Available?"}
Local{"Local Scan<br/>Available?"}
Cache{"Cache Valid?<br/>(< 30 days)"}
StaleCache{"Stale Cache<br/>Exists?"}
HTTPFetch["Fetch from<br/>GitHub Pages"]
FoojayFetch["Query<br/>Disco API"]
LocalScan["Scan Local<br/>JDKs"]
LoadCache["Load from<br/>Cache"]
UseStale["Use Stale<br/>Cache"]
UpdateCache["Update<br/>Cache"]
Return["Return<br/>Metadata"]
Error["Error"]
Request --> Cache
Cache -->|Yes| LoadCache
Cache -->|No| HTTP
HTTP -->|Yes| HTTPFetch
HTTP -->|No| Foojay
HTTPFetch --> UpdateCache
Foojay -->|Yes| FoojayFetch
Foojay -->|No| Local
FoojayFetch --> UpdateCache
Local -->|Yes| LocalScan
Local -->|No| StaleCache
LocalScan --> UpdateCache
StaleCache -->|Yes| UseStale
StaleCache -->|No| Error
LoadCache --> Return
UpdateCache --> Return
UseStale --> Return
style Cache fill:#bbf,stroke:#333,stroke-width:2px
style UpdateCache fill:#bfb,stroke:#333,stroke-width:2px
style Error fill:#fdd,stroke:#333,stroke-width:2px
Metadata features:
- Lazy loading of package details
- Platform-specific filtering
- Distribution-aware caching
- Batch processing capabilities
- 30-day cache TTL with stale fallback
4. Configuration System (src/config.rs
)
Hierarchical configuration with environment overrides:
graph TD
subgraph "Configuration Sources"
TOML["config.toml<br/>(User Config)"]
ENV["Environment<br/>(KOPI_* vars)"]
Defaults["Built-in<br/>Defaults"]
end
subgraph "Configuration Structure"
Storage["Storage Config<br/>(paths, locations)"]
AutoInstall["Auto-install<br/>(enabled, timeout)"]
Shims["Shim Config<br/>(tools, paths)"]
Cache["Cache Config<br/>(TTL, location)"]
Metadata["Metadata Config<br/>(sources, URLs)"]
end
ENV -->|Override| TOML
TOML -->|Override| Defaults
Defaults --> Storage
Defaults --> AutoInstall
Defaults --> Shims
Defaults --> Cache
Defaults --> Metadata
style ENV fill:#ffd,stroke:#333,stroke-width:2px
style TOML fill:#bbf,stroke:#333,stroke-width:2px
5. Storage Layout
~/.kopi/ # KOPI_HOME
├── shims/ # Executable shims
│ ├── java # Shim for java command
│ ├── javac # Shim for javac command
│ └── [other JDK tools]
├── jdks/ # Installed JDKs
│ ├── temurin-21.0.2/ # Distribution-version format
│ │ └── bin/
│ │ ├── java
│ │ └── javac
│ ├── corretto-17.0.9/
│ └── graalvm-21.0.1/
├── cache/ # Metadata cache
│ ├── distributions/ # Per-distribution caches
│ │ ├── temurin.json
│ │ └── corretto.json
│ └── metadata.json # Combined metadata
├── downloads/ # Temporary download directory
├── version # Global default version
└── config.toml # User configuration
Next Steps
- Version Files - Version configuration
- Shims - Command interception details
- Metadata System - JDK discovery and metadata