blob: 3754c0595533ba2dc3d901d852dfd11569a1e52e [file]
# SPDX-License-Identifier: Apache-2.0
#
# Modifications by Apache Solr contributors; see git log for details.
# Licensed under the Apache License, Version 2.0.
#
# The OpenSearch Contributors require contributions made to
# this file be licensed under the Apache-2.0 license or a
# compatible open source license.
# Modifications Copyright OpenSearch Contributors. See
# GitHub history for details.
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import os
import re
from solrorbit import exceptions
from solrorbit.utils import io, process
def _java(java_home):
return io.escape_path(os.path.join(java_home, "bin", "java"))
def supports_option(java_home, option):
"""
Detects support for a specific option (or combination of options) for the JVM version available in java_home.
:param java_home: The JAVA_HOME to use for probing.
:param option: The JVM option or combination of JVM options (separated by spaces) to check.
:return: True iff the provided ``option`` is supported on this JVM.
"""
return process.exit_status_as_bool(
lambda: process.run_subprocess_with_logging("{} {} -version".format(_java(java_home), option)))
def system_property(java_home, system_property_name):
lines = process.run_subprocess_with_output("{} -XshowSettings:properties -version".format(_java(java_home)))
# matches e.g. " java.runtime.version = 1.8.0_121-b13" and captures "1.8.0_121-b13"
sys_prop_pattern = re.compile(r".*%s.*=\s?(.*)" % system_property_name)
for line in lines:
m = sys_prop_pattern.match(line)
if m:
return m.group(1)
return None
def version(java_home, sysprop_reader=system_property):
"""
Determines the version number of JVM available at the provided JAVA_HOME directory.
:param java_home: The JAVA_HOME directory to check.
:param sysprop_reader: (Optional) only relevant for testing.
:return: The version number of the JVM available at ``java_home``.
"""
return sysprop_reader(java_home, "java.version")
def vendor(java_home, sysprop_reader=system_property):
"""
Determines the version number of JVM available at the provided JAVA_HOME directory.
:param java_home: The JAVA_HOME directory to check.
:param sysprop_reader: (Optional) only relevant for testing.
:return: The version number of the JVM available at ``java_home``.
"""
return sysprop_reader(java_home, "java.vm.specification.vendor")
def major_version(java_home, sysprop_reader=system_property):
"""
Determines the major version number of JVM available at the provided JAVA_HOME directory.
:param java_home: The JAVA_HOME directory to check.
:param sysprop_reader: (Optional) only relevant for testing.
:return: An int, representing the major version number of the JVM available at ``java_home``.
"""
v = sysprop_reader(java_home, "java.vm.specification.version")
# are we under the "old" (pre Java 9) or the new (Java 9+) version scheme?
if v.startswith("1."):
return int(v[2])
else:
return int(v)
def is_early_access_release(java_home, sysprop_reader=system_property):
"""
Determines whether the JVM available at the provided JAVA_HOME directory is an early access release. It mimicks the corresponding
bootstrap check in the upstream project.
:param java_home: The JAVA_HOME directory to check.
:param sysprop_reader: (Optional) only relevant for testing.
:return: True iff the JVM available at ``java_home`` is classified as an early access release.
"""
return vendor(java_home, sysprop_reader) == "Oracle Corporation" and version(java_home, sysprop_reader).endswith("-ea")
def resolve_path(majors, sysprop_reader=system_property):
"""
Resolves the path to the JDK with the provided major version(s). It checks the versions in the same order specified in ``majors``
and will return the first match. To achieve this, it first checks the major version x in the environment variable ``JAVAx_HOME``
and falls back to ``JAVA_HOME``. It also ensures that the environment variable points to the right JDK version.
If no appropriate version is found, a ``SystemSetupError`` is raised.
:param majors: Either a list of major versions to check or a single version as an ``int``.
:param sysprop_reader: (Optional) only relevant for testing.
:return: A tuple of (major version, path to Java home directory).
"""
if isinstance(majors, int):
return majors, _resolve_single_path(majors, sysprop_reader=sysprop_reader)
else:
for major in majors:
java_home = _resolve_single_path(major, mandatory=False, sysprop_reader=sysprop_reader)
if java_home:
return major, java_home
raise exceptions.SystemSetupError("Install a JDK with one of the versions {} and point to it with one of {}."
.format(majors, _checked_env_vars(majors)))
def _resolve_single_path(major, mandatory=True, sysprop_reader=system_property):
"""
Resolves the path to a JDK with the provided major version.
:param major: The major version to check.
:param mandatory: Determines if we expect to find a matching JDK.
:param sysprop_reader: (Optional) only relevant for testing.
:return: The resolved path to the JDK or ``None`` if ``mandatory`` is ``False`` and no appropriate JDK has been found.
"""
def do_resolve(env_var, major):
java_v_home = os.getenv(env_var)
if java_v_home:
actual_major = major_version(java_v_home, sysprop_reader)
if actual_major == major:
return java_v_home
elif mandatory:
raise exceptions.SystemSetupError("{} points to JDK {} but it should point to JDK {}.".format(env_var, actual_major, major))
else:
return None
else:
return None
# this has to be consistent with _checked_env_vars()
specific_env_var = "JAVA{}_HOME".format(major)
generic_env_var = "JAVA_HOME"
java_home = do_resolve(specific_env_var, major)
if java_home:
return java_home
else:
java_home = do_resolve(generic_env_var, major)
if java_home:
return java_home
elif mandatory:
raise exceptions.SystemSetupError("Neither {} nor {} point to a JDK {} installation.".
format(specific_env_var, generic_env_var, major))
else:
return None
def _checked_env_vars(majors):
"""
Provides a list of environment variables that are checked for the given list of major versions.
:param majors: A list of major versions.
:return: A list of checked environment variables.
"""
checked = ["JAVA{}_HOME".format(major) for major in majors]
checked.append("JAVA_HOME")
return checked