@@ -6152,12 +6152,13 @@ def _get_raw_stacks_from_api(self):
61526152 return list(self._client.provider.get_web_app_stacks(stack_os_type=None))
61536153
61546154 def _parse_raw_stacks(self, stacks):
6155+ seen_runtimes = set() # Track seen runtime display names to avoid duplicates
61556156 for lang in stacks:
61566157 for major_version in lang.major_versions:
61576158 if self._linux:
61586159 if lang.display_text.lower() == "java":
61596160 continue
6160- self._parse_major_version_linux(major_version, self._stacks)
6161+ self._parse_major_version_linux(major_version, self._stacks, seen_runtimes )
61616162 if self._windows:
61626163 self._parse_major_version_windows(major_version, self._stacks, self.windows_config_mappings)
61636164
@@ -6302,17 +6303,100 @@ def _filter(minor_version):
63026303 return cls._is_valid_runtime_setting(cls._get_runtime_setting(minor_version, linux, java))
63036304 return [m for m in major_version.minor_versions if _filter(m)]
63046305
6306+ @staticmethod
6307+ def _get_java_versions_from_minor_versions(minor_versions):
6308+ """Dynamically extract unique Java versions from minor version values.
6309+ Used for Linux Java SE containers where minor.value is like "25.0.0", "21.0.0".
6310+ Returns versions sorted in descending order (newest first)."""
6311+ java_versions = set()
6312+ for minor in minor_versions:
6313+ # minor.value is like "25.0.0", "21.0.0", "17.0.0", "11.0.0", "8.0.0" or "1.8.0"
6314+ value = minor.value
6315+ if value:
6316+ # Handle both "1.8" format and newer "25", "21" formats
6317+ if value.startswith("1.8"):
6318+ java_versions.add("1.8")
6319+ else:
6320+ # Extract major version number (e.g., "25" from "25.0.0")
6321+ major_ver = value.split('.')[0]
6322+ if major_ver.isdigit():
6323+ java_versions.add(major_ver)
6324+ # Sort descending (newest versions first), treating "1.8" specially
6325+ return sorted(java_versions, key=lambda x: -1 if x == "1.8" else -int(x))
6326+
6327+ @staticmethod
6328+ def _get_java_versions_from_windows_container(container_settings):
6329+ """Dynamically extract Java versions from Windows container settings.
6330+ Looks at the 'runtimes' array in additional_properties.
6331+ Returns versions sorted in descending order (newest first)."""
6332+ java_versions = set()
6333+ additional_props = getattr(container_settings, 'additional_properties', {}) or {}
6334+ runtimes_array = additional_props.get('runtimes', [])
6335+
6336+ for runtime_info in runtimes_array:
6337+ version = runtime_info.get('runtimeVersion')
6338+ if version:
6339+ # Normalize version: convert "1.8" to "1.8", keep others as-is
6340+ java_versions.add(version)
6341+
6342+ # Sort descending (newest versions first), treating "1.8" specially
6343+ return sorted(java_versions, key=lambda x: -1 if x == "1.8" else -int(x))
6344+
6345+ @staticmethod
6346+ def _get_java_runtimes_from_container_settings(container_settings):
6347+ """Dynamically extract Java runtimes from container settings.
6348+ Prefers the 'runtimes' array from the API when available (most future-proof),
6349+ falls back to individual java*Runtime properties in additional_properties,
6350+ and finally SDK-defined properties (java8_runtime, java11_runtime).
6351+ Returns list of tuples: (runtime_name, version, is_auto_update)"""
6352+ runtimes = []
6353+ is_auto_update = getattr(container_settings, 'is_auto_update', False)
6354+ additional_props = getattr(container_settings, 'additional_properties', {}) or {}
6355+
6356+ # Prefer the 'runtimes' array if available (cleanest, most future-proof)
6357+ runtimes_array = additional_props.get('runtimes', [])
6358+ if runtimes_array:
6359+ for runtime_info in runtimes_array:
6360+ runtime_name = runtime_info.get('runtime')
6361+ version = runtime_info.get('runtimeVersion')
6362+ if runtime_name and version:
6363+ runtimes.append((runtime_name, version, is_auto_update))
6364+ else:
6365+ # Fallback: Get runtimes from additional_properties (java*Runtime keys)
6366+ for key, value in additional_props.items():
6367+ # Match pattern like "java25Runtime", "java21Runtime", etc.
6368+ if key.startswith('java') and key.endswith('Runtime') and value:
6369+ # Extract version number from key (e.g., "25" from "java25Runtime")
6370+ version = key[4:-7] # Remove "java" prefix and "Runtime" suffix
6371+ if version.isdigit():
6372+ runtimes.append((value, version, is_auto_update))
6373+
6374+ # Also get runtimes from SDK-defined properties (java8_runtime, java11_runtime)
6375+ if getattr(container_settings, 'java11_runtime', None):
6376+ # Avoid duplicates if already found in additional_properties
6377+ if not any(v == "11" for _, v, _ in runtimes):
6378+ runtimes.append((container_settings.java11_runtime, "11", is_auto_update))
6379+ if getattr(container_settings, 'java8_runtime', None):
6380+ if not any(v == "8" for _, v, _ in runtimes):
6381+ runtimes.append((container_settings.java8_runtime, "8", is_auto_update))
6382+
6383+ # Sort by version descending (newest first)
6384+ runtimes.sort(key=lambda x: -1 if x[1] == "8" else -int(x[1]))
6385+ return runtimes
6386+
63056387 def _parse_major_version_windows(self, major_version, parsed_results, config_mappings):
63066388 java_container_minor_versions = self._get_valid_minor_versions(major_version, linux=False, java=True)
63076389 if java_container_minor_versions:
6308- javas = ["21", "17", "11", "1.8"]
6390+ # Limit to 3 containers for display
63096391 if len(java_container_minor_versions) > 0:
63106392 leng = len(java_container_minor_versions) if len(java_container_minor_versions) < 3 else 3
63116393 java_container_minor_versions = java_container_minor_versions[:leng]
63126394 for container in java_container_minor_versions:
63136395 container_settings = container.stack_settings.windows_container_settings
63146396 java_container = container_settings.java_container
63156397 container_version = container_settings.java_container_version
6398+ # Get Java versions from the container's runtimes array
6399+ javas = self._get_java_versions_from_windows_container(container_settings)
63166400 for java in javas:
63176401 runtime = self.get_windows_java_runtime(
63186402 java,
@@ -6380,11 +6464,13 @@ def get_windows_java_runtime(self, java_version=None,
63806464 linux=False,
63816465 is_auto_update=is_auto_update)
63826466
6383- def _parse_major_version_linux(self, major_version, parsed_results):
6467+ def _parse_major_version_linux(self, major_version, parsed_results, seen_runtimes ):
63846468 minor_java_container_versions = self._get_valid_minor_versions(major_version, linux=True, java=True)
63856469 if "SE" in major_version.display_text:
6386- se_containers = [minor_java_container_versions[0]]
6387- for java in ["21", "17", "11", "1.8"]:
6470+ # Dynamically get Java versions from the available minor versions
6471+ java_versions = self._get_java_versions_from_minor_versions(minor_java_container_versions)
6472+ se_containers = [minor_java_container_versions[0]] if minor_java_container_versions else []
6473+ for java in java_versions:
63886474 se_java_containers = [c for c in minor_java_container_versions if c.value.startswith(java)]
63896475 se_containers = se_containers + se_java_containers[:len(se_java_containers) if len(se_java_containers) < 2 else 2] # pylint: disable=line-too-long
63906476 minor_java_container_versions = se_containers
@@ -6394,14 +6480,15 @@ def _parse_major_version_linux(self, major_version, parsed_results):
63946480 "SE" not in major_version.display_text else len(minor_java_container_versions)
63956481 for minor in minor_java_container_versions[:leng]:
63966482 linux_container_settings = minor.stack_settings.linux_container_settings
6397- runtimes = [
6398- (linux_container_settings.additional_properties.get("java21Runtime"), "21", linux_container_settings.is_auto_update), # pylint: disable=line-too-long
6399- (linux_container_settings.additional_properties.get("java17Runtime"), "17", linux_container_settings.is_auto_update), # pylint: disable=line-too-long
6400- (linux_container_settings.java11_runtime, "11", linux_container_settings.is_auto_update),
6401- (linux_container_settings.java8_runtime, "8", linux_container_settings.is_auto_update)]
6483+ # Dynamically get all Java runtimes from container settings
6484+ runtimes = self._get_java_runtimes_from_container_settings(linux_container_settings)
64026485 # Remove the JBoss'_byol' entries from the output
64036486 runtimes = [(r, v, au) for (r, v, au) in runtimes if r is not None and not r.endswith("_byol")] # pylint: disable=line-too-long
64046487 for runtime_name, version, auto_update in [(r, v, au) for (r, v, au) in runtimes if r is not None]:
6488+ # Skip duplicates
6489+ if runtime_name in seen_runtimes:
6490+ continue
6491+ seen_runtimes.add(runtime_name)
64056492 runtime = self.Runtime(display_name=runtime_name,
64066493 configs={"linux_fx_version": runtime_name},
64076494 github_actions_properties={"github_actions_version": version},
0 commit comments