summaryrefslogtreecommitdiff
path: root/docs/handbook/meta/java-runtime-metadata.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/handbook/meta/java-runtime-metadata.md')
-rw-r--r--docs/handbook/meta/java-runtime-metadata.md546
1 files changed, 546 insertions, 0 deletions
diff --git a/docs/handbook/meta/java-runtime-metadata.md b/docs/handbook/meta/java-runtime-metadata.md
new file mode 100644
index 0000000000..dc9ca8a5ea
--- /dev/null
+++ b/docs/handbook/meta/java-runtime-metadata.md
@@ -0,0 +1,546 @@
+# Meta — Java Runtime Metadata
+
+## Overview
+
+Meta aggregates Java runtime information from **four vendors** and produces unified metadata for the launcher. This allows the launcher to automatically download and manage Java installations across platforms and architectures.
+
+### Vendors and Components
+
+| Vendor | Component UID | API Source | JVM Implementation |
+|---|---|---|---|
+| Mojang | `net.minecraft.java` | `launchermeta.mojang.com` | HotSpot |
+| Adoptium (Eclipse Temurin) | `net.adoptium.java` | `api.adoptium.net` | HotSpot |
+| OpenJ9 (IBM Semeru) | `com.ibm.java` | `api.adoptopenjdk.net` | OpenJ9 |
+| Azul (Zulu) | `com.azul.java` | `api.azul.com` | HotSpot |
+
+Each vendor gets its own component UID and separate output files. Mojang's component (`net.minecraft.java`) is special: it is the primary component used by the launcher and is augmented with data from Adoptium and Azul for platforms that Mojang itself doesn't cover.
+
+---
+
+## Phase 1: Update — `update_java.py`
+
+### Adoptium (Eclipse Temurin)
+
+Fetches from the Adoptium v3 API:
+
+```python
+ADOPTIUM_API_BASE = "https://api.adoptium.net"
+ADOPTX_API_AVAILABLE_RELEASES = f"{{base_url}}/v3/info/available_releases"
+ADOPTX_API_FEATURE_RELEASES = (
+ f"{{base_url}}/v3/assets/feature_releases/{{feature_version}}/{{release_type}}"
+)
+```
+
+**Step 1**: Get available feature versions:
+```python
+r = sess.get(ADOPTX_API_AVAILABLE_RELEASES.format(base_url=ADOPTIUM_API_BASE))
+available = AdoptxAvailableReleases(**r.json())
+```
+
+**Step 2**: For each feature version, paginate through releases:
+```python
+for feature in available.available_releases:
+ page = 0
+ while True:
+ query = AdoptxAPIFeatureReleasesQuery(
+ image_type=AdoptxImageType.Jre,
+ page_size=page_size,
+ page=page,
+ jvm_impl=AdoptxJvmImpl.Hotspot,
+ vendor=AdoptxVendor.Eclipse,
+ )
+ api_call = adoptiumAPIFeatureReleasesUrl(feature, query=query)
+ r_rls = sess.get(api_call)
+ # ...
+ if len(r_rls.json()) < page_size:
+ break
+ page += 1
+```
+
+**Step 3**: Save as `AdoptxReleases` per feature version:
+```python
+releases = AdoptxReleases(__root__=releases_for_feature)
+releases.write(feature_file)
+```
+
+**Step 4**: Write filtered available releases:
+```python
+filtered_available_releases(available, present_adoptium_features).write(
+ available_releases_file
+)
+```
+
+The `filtered_available_releases()` function removes features that had no actual releases:
+
+```python
+def filtered_available_releases(
+ available: AdoptxAvailableReleases, present_features: list[int]
+) -> AdoptxAvailableReleases:
+ filtered_features = sorted(set(present_features))
+ filtered_lts = [
+ feature for feature in available.available_lts_releases
+ if feature in filtered_features
+ ]
+ # ...
+```
+
+### Retry Logic
+
+All vendor APIs use a 3-attempt retry pattern with linear backoff for server errors (5xx):
+
+```python
+for attempt in range(3):
+ r = sess.get(api_call)
+ if r.status_code >= 500:
+ if attempt < 2:
+ time.sleep(1 * (attempt + 1))
+ continue
+ else:
+ r.raise_for_status()
+ else:
+ r.raise_for_status()
+ break
+```
+
+### OpenJ9 (IBM Semeru)
+
+Uses the same API structure as Adoptium but with a different base URL and vendor:
+
+```python
+OPENJ9_API_BASE = "https://api.adoptopenjdk.net"
+```
+
+```python
+query = AdoptxAPIFeatureReleasesQuery(
+ image_type=AdoptxImageType.Jre,
+ jvm_impl=AdoptxJvmImpl.OpenJ9,
+ vendor=AdoptxVendor.Ibm,
+)
+api_call = openj9APIFeatureReleasesUrl(feature, query=query)
+```
+
+The model classes `AdoptxRelease`, `AdoptxBinary`, `AdoptxVersion` are shared between Adoptium and OpenJ9 (the "adoptx" prefix covers both).
+
+### Azul (Zulu)
+
+Uses a completely different API structure:
+
+```python
+AZUL_API_BASE = "https://api.azul.com/metadata/v1"
+AZUL_API_PACKAGES = f"{AZUL_API_BASE}/zulu/packages/"
+AZUL_API_PACKAGE_DETAIL = f"{AZUL_API_BASE}/zulu/packages/{{package_uuid}}"
+```
+
+**Step 1**: Paginate through Zulu package listings:
+```python
+query = AzulApiPackagesQuery(
+ archive_type=AzulArchiveType.Zip,
+ release_status=AzulReleaseStatus.Ga,
+ availability_types=[AzulAvailabilityType.CA],
+ java_package_type=AzulJavaPackageType.Jre,
+ javafx_bundled=False,
+ latest=True,
+ page=page,
+ page_size=page_size,
+)
+```
+
+**Step 2**: For each package, fetch detailed info (or use cached):
+```python
+pkg_file = os.path.join(UPSTREAM_DIR, AZUL_VERSIONS_DIR, f"{pkg.package_uuid}.json")
+if os.path.exists(pkg_file):
+ pkg_detail = ZuluPackageDetail.parse_file(pkg_file)
+else:
+ api_call = azulApiPackageDetailUrl(pkg.package_uuid)
+ r_pkg = sess.get(api_call)
+ pkg_detail = ZuluPackageDetail(**r_pkg.json())
+ pkg_detail.write(pkg_file)
+```
+
+---
+
+## Phase 2: Generate — `generate_java.py`
+
+### Architecture and OS Translation
+
+The generator normalizes vendor-specific OS and architecture names to a unified `JavaRuntimeOS` enum:
+
+```python
+MOJANG_OS_ARCHITECTURE_TRANSLATIONS = {
+ 64: "x64",
+ 32: "x86",
+ "x32": "x86",
+ "i386": "x86",
+ "aarch64": "arm64",
+ "x86_64": "x64",
+ "arm": "arm32",
+ "riscv64": "riscv64",
+}
+
+MOJANG_OS_TRANSLATIONS = {
+ "osx": "mac-os",
+ "mac": "mac-os",
+ "macos": "mac-os",
+}
+```
+
+`JavaRuntimeOS` combines OS and architecture into a single enum value like `linux-x64`, `windows-arm64`, `mac-os-arm64`.
+
+### Vendor-Specific Converters
+
+Each vendor has a dedicated conversion function that maps vendor data to the unified `JavaRuntimeMeta` model:
+
+#### Mojang Converter
+
+```python
+def mojang_runtime_to_java_runtime(
+ mojang_runtime: MojangJavaRuntime,
+ mojang_component: MojangJavaComponent,
+ runtime_os: JavaRuntimeOS,
+) -> JavaRuntimeMeta:
+ # Parse version strings like "8u422" or "17.0.12"
+ major, _, trail = mojang_runtime.version.name.partition("u")
+ # ...
+ return JavaRuntimeMeta(
+ name=mojang_component,
+ vendor="mojang",
+ url=mojang_runtime.manifest.url,
+ downloadType=JavaRuntimeDownloadType.Manifest,
+ packageType=JavaPackageType.Jre,
+ # ...
+ )
+```
+
+Key: Mojang uses `downloadType="manifest"` (a JSON manifest of individual files), while other vendors use `downloadType="archive"` (a ZIP/tar.gz download).
+
+#### Adoptium/OpenJ9 Converter
+
+```python
+def adoptx_release_binary_to_java_runtime(
+ rls: AdoptxRelease,
+ binary: AdoptxBinary,
+ runtime_os: JavaRuntimeOS,
+) -> JavaRuntimeMeta:
+ # ...
+ if rls.vendor == "eclipse":
+ rls_distribution = "temurin"
+ elif rls.vendor == "ibm":
+ rls_distribution = "semeru-open"
+
+ rls_name = f"{rls.vendor}_{rls_distribution}_{binary.image_type}{version}"
+ return JavaRuntimeMeta(
+ vendor=rls.vendor,
+ url=binary.package.link,
+ downloadType=JavaRuntimeDownloadType.Archive,
+ # ...
+ )
+```
+
+#### Azul Converter
+
+```python
+def azul_package_to_java_runtime(
+ pkg: ZuluPackageDetail, runtime_os: JavaRuntimeOS
+) -> JavaRuntimeMeta:
+ # ...
+ rls_name = f"azul_{pkg.product}_{pkg.java_package_type}{version}"
+ return JavaRuntimeMeta(
+ vendor="azul",
+ url=pkg.download_url,
+ downloadType=JavaRuntimeDownloadType.Archive,
+ # ...
+ )
+```
+
+### Mojang Java Component Mapping
+
+Mojang names Java runtime groups by Greek letters. The generator maps these to major versions:
+
+```python
+def mojang_component_to_major(mojang_component: MojangJavaComponent) -> int:
+ match mojang_component:
+ case MojangJavaComponent.JreLegacy: return 8
+ case MojangJavaComponent.Alpha: return 17
+ case MojangJavaComponent.Beta: return 17
+ case MojangJavaComponent.Gamma: return 17
+ case MojangJavaComponent.GammaSnapshot: return 17
+ case MojangJavaComponent.Exe: return 0
+ case MojangJavaComponent.Delta: return 21
+```
+
+### Extra Mojang Java (Platform Gap Filling)
+
+Mojang's Java manifest doesn't cover all platforms. The generator fills gaps using Adoptium and Azul data:
+
+```python
+def add_java_runtime(runtime: JavaRuntimeMeta, major: int):
+ javas[major].append(runtime)
+
+ # Track runtimes for platforms Mojang doesn't cover
+ if (
+ (runtime.runtime_os in [JavaRuntimeOS.MacOsArm64, JavaRuntimeOS.WindowsArm64]
+ and major == 8)
+ or (runtime.runtime_os in [
+ JavaRuntimeOS.WindowsArm32, JavaRuntimeOS.LinuxArm32,
+ JavaRuntimeOS.LinuxArm64,
+ ] and major in [8, 17, 21])
+ or (runtime.runtime_os in [JavaRuntimeOS.LinuxX86, JavaRuntimeOS.LinuxRiscv64]
+ and major in [17, 21, 25])
+ ):
+ extra_mojang_javas[major].append(runtime)
+```
+
+After processing all vendors, these extras are injected into `net.minecraft.java`:
+
+```python
+for java_os in [
+ JavaRuntimeOS.WindowsArm32,
+ JavaRuntimeOS.LinuxArm32,
+ JavaRuntimeOS.LinuxArm64,
+ JavaRuntimeOS.LinuxRiscv64,
+]:
+ for comp in [
+ MojangJavaComponent.JreLegacy,
+ MojangJavaComponent.Alpha,
+ MojangJavaComponent.Beta,
+ MojangJavaComponent.Gamma,
+ MojangJavaComponent.GammaSnapshot,
+ MojangJavaComponent.Delta,
+ ]:
+ runtime = get_mojang_extra_java(comp, java_os)
+ if runtime != None:
+ add_java_runtime(runtime, mojang_component_to_major(comp))
+```
+
+The `get_mojang_extra_java()` function prefers Adoptium over Azul, selects the latest version:
+
+```python
+def get_mojang_extra_java(
+ mojang_component: MojangJavaComponent, java_os: JavaRuntimeOS
+) -> JavaRuntimeMeta | None:
+ posible_javas = list(
+ filter(lambda x: x.runtime_os == java_os, extra_mojang_javas[java_major])
+ )
+ prefered_vendor = list(filter(lambda x: x.vendor != "azul", posible_javas))
+ if len(prefered_vendor) == 0:
+ prefered_vendor = posible_javas
+ prefered_vendor.sort(key=lambda x: x.version, reverse=True)
+ runtime = prefered_vendor[0]
+ runtime.name = mojang_component
+ return runtime
+```
+
+### Writing Output
+
+```python
+def writeJavas(javas: dict[int, list[JavaRuntimeMeta]], uid: str):
+ javas = dict(sorted(javas.items(), key=lambda item: item[0]))
+
+ for major, runtimes in javas.items():
+ version_file = os.path.join(LAUNCHER_DIR, uid, f"java{major}.json")
+ java_version = JavaRuntimeVersion(
+ name=f"Java {major}",
+ uid=uid,
+ version=f"java{major}",
+ releaseTime=timestamps.get(major),
+ runtimes=runtimes,
+ )
+ java_version.write(version_file)
+
+ package = MetaPackage(uid=uid, name="Java Runtimes", recommended=[])
+ package.write(os.path.join(LAUNCHER_DIR, uid, "package.json"))
+```
+
+`JavaRuntimeVersion` extends `MetaVersion` with a `runtimes: list[JavaRuntimeMeta]` field.
+
+Timestamps are derived from the **oldest** runtime in each major version to ensure monotonically increasing release times:
+
+```python
+releaseTime = reduce(
+ oldest_timestamp,
+ (runtime.release_time for runtime in runtimes),
+ None,
+)
+if prevDate is not None and releaseTime < prevDate:
+ releaseTime = prevDate + datetime.timedelta(seconds=1)
+```
+
+---
+
+## Key Data Models
+
+### `JavaRuntimeOS` (StrEnum)
+
+Platform identifiers combining OS and architecture:
+
+| Value | Platform |
+|---|---|
+| `mac-os-x64` | macOS Intel |
+| `mac-os-arm64` | macOS Apple Silicon |
+| `linux-x64` | Linux x86_64 |
+| `linux-x86` | Linux 32-bit |
+| `linux-arm64` | Linux AArch64 |
+| `linux-arm32` | Linux ARM 32-bit |
+| `linux-riscv64` | Linux RISC-V 64 |
+| `windows-x64` | Windows 64-bit |
+| `windows-x86` | Windows 32-bit |
+| `windows-arm64` | Windows ARM 64-bit |
+| `windows-arm32` | Windows ARM 32-bit |
+
+### `JavaVersionMeta`
+
+Structured Java version (supports comparison with `@total_ordering`):
+
+```python
+class JavaVersionMeta(MetaBase):
+ major: int
+ minor: int
+ security: int
+ build: Optional[int] = None
+ buildstr: Optional[str] = None
+ name: Optional[str] = None
+```
+
+Example: Java `17.0.12+7` → `major=17, minor=0, security=12, build=7`.
+
+### `JavaRuntimeMeta`
+
+The core runtime descriptor:
+
+```python
+class JavaRuntimeMeta(MetaBase):
+ name: str # e.g., "eclipse_temurin_jre17.0.12"
+ vendor: str # "mojang", "eclipse", "ibm", "azul"
+ url: str # Download URL
+ release_time: datetime
+ checksum: Optional[JavaChecksumMeta]
+ download_type: JavaRuntimeDownloadType # "manifest" or "archive"
+ package_type: JavaPackageType # "jre" or "jdk"
+ version: JavaVersionMeta
+ runtime_os: JavaRuntimeOS
+```
+
+### `JavaRuntimeVersion`
+
+Extends `MetaVersion` with a runtime list:
+
+```python
+class JavaRuntimeVersion(MetaVersion):
+ runtimes: list[JavaRuntimeMeta]
+```
+
+### `AdoptxRelease` / `AdoptxBinary`
+
+Shared models for Adoptium and OpenJ9:
+
+```python
+class AdoptxBinary(MetaBase):
+ os: str
+ architecture: AdoptxArchitecture
+ image_type: AdoptxImageType
+ package: Optional[AdoptxPackage]
+ jvm_impl: AdoptxJvmImpl
+ heap_size: AdoptxHeapSize
+
+class AdoptxRelease(MetaBase):
+ release_id: str = Field(alias="id")
+ timestamp: datetime
+ binaries: list[AdoptxBinary]
+ vendor: AdoptxVendor
+ version_data: AdoptxVersion
+```
+
+### `ZuluPackageDetail`
+
+Azul's detailed package info:
+
+```python
+class ZuluPackageDetail(MetaBase):
+ package_uuid: str
+ sha256_hash: Optional[str]
+ download_url: str
+ java_version: list[int]
+ java_package_type: AzulJavaPackageType
+ os: AzulOs
+ arch: AzulArch
+ hw_bitness: AzulHwBitness
+ archive_type: AzulArchiveType
+```
+
+---
+
+## Output Structure
+
+```
+launcher/
+├── net.minecraft.java/
+│ ├── package.json
+│ ├── java8.json
+│ ├── java17.json
+│ └── java21.json
+├── net.adoptium.java/
+│ ├── package.json
+│ ├── java8.json
+│ ├── java11.json
+│ ├── java17.json
+│ └── java21.json
+├── com.ibm.java/
+│ ├── package.json
+│ ├── java8.json
+│ └── java11.json
+└── com.azul.java/
+ ├── package.json
+ ├── java8.json
+ ├── java11.json
+ ├── java17.json
+ └── java21.json
+```
+
+---
+
+## Upstream Data Structure
+
+```
+upstream/java_runtime/
+├── adoptium/
+│ ├── available_releases.json
+│ └── versions/
+│ ├── java8.json
+│ ├── java11.json
+│ └── ...
+├── ibm/
+│ ├── available_releases.json
+│ └── versions/
+│ ├── java8.json
+│ └── ...
+└── azul/
+ ├── packages.json
+ └── versions/
+ ├── <package-uuid>.json
+ ├── java8.json
+ └── ...
+```
+
+---
+
+## Processing Pipeline Summary
+
+```
+Vendor APIs ──► update_java.py ──► upstream/java_runtime/
+ │
+ ▼
+ generate_java.py
+ │
+ ┌───────────────┼───────────────┐
+ ▼ ▼ ▼
+ net.adoptium.java com.ibm.java com.azul.java
+ │ │ │
+ └───── extra_mojang_javas ──────┘
+ │
+ ▼
+ net.minecraft.java
+ (augmented with third-party
+ runtimes for ARM & RISC-V)
+```
+
+The Mojang component is always processed **last** so it can pull in third-party runtimes for platforms Mojang doesn't natively support.