• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

localstack / localstack / 7ae8c08b-27e7-4df6-bfb9-4892ae974ff7

17 Mar 2025 11:44PM UTC coverage: 86.954% (+0.02%) from 86.93%
7ae8c08b-27e7-4df6-bfb9-4892ae974ff7

push

circleci

web-flow
SNS: fix Filter Policy engine to not evaluate full complex payload (#12395)

8 of 8 new or added lines in 1 file covered. (100.0%)

27 existing lines in 8 files now uncovered.

62326 of 71677 relevant lines covered (86.95%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.03
/localstack-core/localstack/packages/java.py
1
import logging
1✔
2
import os
1✔
3
from typing import List
1✔
4

5
import requests
1✔
6

7
from localstack.constants import USER_AGENT_STRING
1✔
8
from localstack.packages import InstallTarget, Package
1✔
9
from localstack.packages.core import ArchiveDownloadAndExtractInstaller
1✔
10
from localstack.utils.files import rm_rf
1✔
11
from localstack.utils.platform import Arch, get_arch, is_linux, is_mac_os
1✔
12
from localstack.utils.run import run
1✔
13

14
LOG = logging.getLogger(__name__)
1✔
15

16
# Default version if not specified
17
DEFAULT_JAVA_VERSION = "11"
1✔
18

19
# Supported Java LTS versions mapped with Eclipse Temurin build semvers
20
JAVA_VERSIONS = {
1✔
21
    "8": "8u432-b06",
22
    "11": "11.0.25+9",
23
    "17": "17.0.13+11",
24
    "21": "21.0.5+11",
25
}
26

27

28
class JavaInstallerMixin:
1✔
29
    """
30
    Mixin class for packages that depend on Java. It introduces methods that install Java and help build environment.
31
    """
32

33
    def _prepare_installation(self, target: InstallTarget) -> None:
1✔
UNCOV
34
        java_package.install(target=target)
×
35

36
    def get_java_home(self) -> str | None:
1✔
37
        """
38
        Returns path to JRE installation.
39
        """
40
        return java_package.get_installer().get_java_home()
1✔
41

42
    def get_java_lib_path(self) -> str | None:
1✔
43
        """
44
        Returns the path to the Java shared library.
45
        """
46
        if java_home := self.get_java_home():
1✔
47
            if is_mac_os():
1✔
48
                return os.path.join(java_home, "lib", "jli", "libjli.dylib")
×
49
            return os.path.join(java_home, "lib", "server", "libjvm.so")
1✔
50

51
    def get_java_env_vars(self, path: str = None, ld_library_path: str = None) -> dict[str, str]:
1✔
52
        """
53
        Returns environment variables pointing to the Java installation. This is useful to build the environment where
54
        the application will run.
55

56
        :param path: If not specified, the value of PATH will be obtained from the environment
57
        :param ld_library_path: If not specified, the value of LD_LIBRARY_PATH will be obtained from the environment
58
        :return: dict consisting of two items:
59
            - JAVA_HOME: path to JRE installation
60
            - PATH: the env path variable updated with JRE bin path
61
        """
62
        java_home = self.get_java_home()
×
63
        java_bin = f"{java_home}/bin"
×
64

65
        path = path or os.environ["PATH"]
×
66

67
        ld_library_path = ld_library_path or os.environ.get("LD_LIBRARY_PATH")
×
68
        # null paths (e.g. `:/foo`) have a special meaning according to the manpages
69
        if ld_library_path is None:
×
70
            ld_library_path = f"{java_home}/lib:{java_home}/lib/server"
×
71
        else:
72
            ld_library_path = f"{java_home}/lib:{java_home}/lib/server:{ld_library_path}"
×
73

74
        return {
×
75
            "JAVA_HOME": java_home,
76
            "LD_LIBRARY_PATH": ld_library_path,
77
            "PATH": f"{java_bin}:{path}",
78
        }
79

80

81
class JavaPackageInstaller(ArchiveDownloadAndExtractInstaller):
1✔
82
    def __init__(self, version: str):
1✔
83
        super().__init__("java", version, extract_single_directory=True)
1✔
84

85
    def _get_install_marker_path(self, install_dir: str) -> str:
1✔
86
        if is_mac_os():
1✔
87
            return os.path.join(install_dir, "Contents", "Home", "bin", "java")
×
88
        return os.path.join(install_dir, "bin", "java")
1✔
89

90
    def _get_download_url(self) -> str:
1✔
91
        # Note: Eclipse Temurin does not provide Mac aarch64 Java 8 builds.
92
        # See https://adoptium.net/en-GB/supported-platforms/
93
        try:
1✔
94
            LOG.debug("Determining the latest Java build version")
1✔
95
            return self._download_url_latest_release()
1✔
96
        except Exception as exc:  # noqa
×
97
            LOG.debug(
×
98
                "Unable to determine the latest Java build version. Using pinned versions: %s", exc
99
            )
100
            return self._download_url_fallback()
×
101

102
    def _post_process(self, target: InstallTarget) -> None:
1✔
103
        target_directory = self._get_install_dir(target)
1✔
104
        minimal_jre_path = os.path.join(target.value, self.name, f"{self.version}.minimal")
1✔
105
        rm_rf(minimal_jre_path)
1✔
106

107
        # If jlink is not available, use the environment as is
108
        if not os.path.exists(os.path.join(target_directory, "bin", "jlink")):
1✔
109
            LOG.warning("Skipping JRE optimisation because jlink is not available")
×
110
            return
×
111

112
        # Build a custom JRE with only the necessary bits to minimise disk footprint
113
        LOG.debug("Optimising JRE installation")
1✔
114
        cmd = (
1✔
115
            "bin/jlink --add-modules "
116
            # Required modules
117
            "java.base,java.desktop,java.instrument,java.management,"
118
            "java.naming,java.scripting,java.sql,java.xml,jdk.compiler,"
119
            # jdk.unsupported contains sun.misc.Unsafe which is required by some dependencies
120
            "jdk.unsupported,"
121
            # Additional cipher suites
122
            "jdk.crypto.cryptoki,"
123
            # Archive support
124
            "jdk.zipfs,"
125
            # Required by MQ broker
126
            "jdk.httpserver,jdk.management,jdk.management.agent,"
127
            # Required by Spark and Hadoop
128
            "java.security.jgss,jdk.security.auth,"
129
            # Include required locales
130
            "jdk.localedata --include-locales en "
131
            # Supplementary args
132
            "--compress 2 --strip-debug --no-header-files --no-man-pages "
133
            # Output directory
134
            "--output " + minimal_jre_path
135
        )
136
        run(cmd, cwd=target_directory)
1✔
137

138
        rm_rf(target_directory)
1✔
139
        os.rename(minimal_jre_path, target_directory)
1✔
140

141
    def get_java_home(self) -> str | None:
1✔
142
        """
143
        Get JAVA_HOME for this installation of Java.
144
        """
145
        installed_dir = self.get_installed_dir()
1✔
146
        if is_mac_os():
1✔
147
            return os.path.join(installed_dir, "Contents", "Home")
×
148
        return installed_dir
1✔
149

150
    @property
1✔
151
    def arch(self) -> str | None:
1✔
152
        return (
1✔
153
            "x64" if get_arch() == Arch.amd64 else "aarch64" if get_arch() == Arch.arm64 else None
154
        )
155

156
    @property
1✔
157
    def os_name(self) -> str | None:
1✔
158
        return "linux" if is_linux() else "mac" if is_mac_os() else None
1✔
159

160
    def _download_url_latest_release(self) -> str:
1✔
161
        """
162
        Return the download URL for latest stable JDK build.
163
        """
164
        endpoint = (
1✔
165
            f"https://api.adoptium.net/v3/assets/latest/{self.version}/hotspot?"
166
            f"os={self.os_name}&architecture={self.arch}&image_type=jdk"
167
        )
168
        # Override user-agent because Adoptium API denies service to `requests` library
169
        response = requests.get(endpoint, headers={"user-agent": USER_AGENT_STRING}).json()
1✔
170
        return response[0]["binary"]["package"]["link"]
1✔
171

172
    def _download_url_fallback(self) -> str:
1✔
173
        """
174
        Return the download URL for pinned JDK build.
175
        """
176
        semver = JAVA_VERSIONS[self.version]
×
177
        tag_slug = f"jdk-{semver}"
×
178
        semver_safe = semver.replace("+", "_")
×
179

180
        # v8 uses a different tag and version scheme
181
        if self.version == "8":
×
182
            semver_safe = semver_safe.replace("-", "")
×
183
            tag_slug = f"jdk{semver}"
×
184

185
        return (
×
186
            f"https://github.com/adoptium/temurin{self.version}-binaries/releases/download/{tag_slug}/"
187
            f"OpenJDK{self.version}U-jdk_{self.arch}_{self.os_name}_hotspot_{semver_safe}.tar.gz"
188
        )
189

190

191
class JavaPackage(Package):
1✔
192
    def __init__(self, default_version: str = DEFAULT_JAVA_VERSION):
1✔
193
        super().__init__(name="Java", default_version=default_version)
1✔
194

195
    def get_versions(self) -> List[str]:
1✔
196
        return list(JAVA_VERSIONS.keys())
1✔
197

198
    def _get_installer(self, version):
1✔
199
        return JavaPackageInstaller(version)
1✔
200

201

202
java_package = JavaPackage()
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc