[enhancement](java-udf) Support loading libjvm at runtime (#13660)
This commit is contained in:
@ -603,26 +603,13 @@ include_directories(
|
||||
)
|
||||
|
||||
if (BUILD_JAVA_UDF)
|
||||
execute_process(COMMAND chmod 755 ${BASE_DIR}/../tools/find_libjvm.sh)
|
||||
execute_process(COMMAND ${BASE_DIR}/../tools/find_libjvm.sh OUTPUT_VARIABLE LIBJVM_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
FILE(GLOB_RECURSE LIB_JVM ${LIBJVM_PATH})
|
||||
if("${LIB_JVM}" STREQUAL "")
|
||||
message(STATUS "Disable JAVA UDF because there is no libjvm found!")
|
||||
include_directories($ENV{JAVA_HOME}/include)
|
||||
if (NOT OS_MACOSX)
|
||||
include_directories($ENV{JAVA_HOME}/include/linux)
|
||||
else()
|
||||
set(DORIS_DEPENDENCIES
|
||||
${DORIS_DEPENDENCIES}
|
||||
jvm
|
||||
)
|
||||
add_library(jvm SHARED IMPORTED)
|
||||
set_target_properties(jvm PROPERTIES IMPORTED_LOCATION ${LIB_JVM})
|
||||
include_directories($ENV{JAVA_HOME}/include)
|
||||
if (NOT OS_MACOSX)
|
||||
include_directories($ENV{JAVA_HOME}/include/linux)
|
||||
else()
|
||||
include_directories($ENV{JAVA_HOME}/include/darwin)
|
||||
endif()
|
||||
add_definitions("-DLIBJVM")
|
||||
include_directories($ENV{JAVA_HOME}/include/darwin)
|
||||
endif()
|
||||
add_definitions("-DLIBJVM")
|
||||
endif()
|
||||
|
||||
if (NOT OS_MACOSX)
|
||||
|
||||
@ -127,6 +127,10 @@ if (OS_MACOSX)
|
||||
list(APPEND UTIL_FILES perf_counters_mac.cpp disk_info_mac.cpp)
|
||||
endif()
|
||||
|
||||
if (BUILD_JAVA_UDF)
|
||||
list(APPEND UTIL_FILES libjvm_loader.cpp)
|
||||
endif()
|
||||
|
||||
add_library(Util STATIC
|
||||
${UTIL_FILES}
|
||||
)
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "common/config.h"
|
||||
#include "gutil/once.h"
|
||||
#include "gutil/strings/substitute.h"
|
||||
#include "libjvm_loader.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
@ -67,7 +68,7 @@ const std::string GetDorisJNIClasspath() {
|
||||
|
||||
void FindOrCreateJavaVM() {
|
||||
int num_vms;
|
||||
int rv = JNI_GetCreatedJavaVMs(&g_vm, 1, &num_vms);
|
||||
int rv = LibJVMLoader::JNI_GetCreatedJavaVMs(&g_vm, 1, &num_vms);
|
||||
if (rv == 0) {
|
||||
auto classpath = GetDorisJNIClasspath();
|
||||
std::string heap_size = fmt::format("-Xmx{}", config::jvm_max_heap_size);
|
||||
@ -91,7 +92,7 @@ void FindOrCreateJavaVM() {
|
||||
// Set it to JNI_FALSE because JNI_TRUE will let JVM ignore the max size config.
|
||||
vm_args.ignoreUnrecognized = JNI_FALSE;
|
||||
|
||||
jint res = JNI_CreateJavaVM(&g_vm, (void**)&env, &vm_args);
|
||||
jint res = LibJVMLoader::JNI_CreateJavaVM(&g_vm, (void**)&env, &vm_args);
|
||||
if (JNI_OK != res) {
|
||||
DCHECK(false) << "Failed to create JVM, code= " << res;
|
||||
}
|
||||
@ -213,6 +214,8 @@ Status JniUtil::LocalToGlobalRef(JNIEnv* env, jobject local_ref, jobject* global
|
||||
}
|
||||
|
||||
Status JniUtil::Init() {
|
||||
RETURN_IF_ERROR(LibJVMLoader::instance().load());
|
||||
|
||||
// Get the JNIEnv* corresponding to current thread.
|
||||
JNIEnv* env;
|
||||
RETURN_IF_ERROR(JniUtil::GetJNIEnv(&env));
|
||||
|
||||
99
be/src/util/libjvm_loader.cpp
Normal file
99
be/src/util/libjvm_loader.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF 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.
|
||||
|
||||
#include "util/libjvm_loader.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/status.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#ifndef __APPLE__
|
||||
#define LIBJVM_SO "libjvm.so"
|
||||
#else
|
||||
#define LIBJVM_SO "libjvm.dylib"
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
doris::Status resolve_symbol(T& pointer, void* handle, const char* symbol) {
|
||||
pointer = reinterpret_cast<T>(dlsym(handle, symbol));
|
||||
return (pointer != nullptr)
|
||||
? doris::Status::OK()
|
||||
: doris::Status::RuntimeError("Failed to resolve the symbol %s", symbol);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace doris {
|
||||
|
||||
LibJVMLoader::JNI_GetCreatedJavaVMsPointer LibJVMLoader::JNI_GetCreatedJavaVMs = nullptr;
|
||||
LibJVMLoader::JNI_CreateJavaVMPointer LibJVMLoader::JNI_CreateJavaVM = nullptr;
|
||||
|
||||
LibJVMLoader& LibJVMLoader::instance() {
|
||||
static std::once_flag find_library;
|
||||
static std::string library;
|
||||
std::call_once(find_library, []() {
|
||||
const auto* java_home = getenv("JAVA_HOME");
|
||||
if (!java_home) {
|
||||
return;
|
||||
}
|
||||
std::string path(java_home);
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
|
||||
if (entry.path().filename() == LIBJVM_SO) {
|
||||
library = entry.path().string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
static LibJVMLoader loader(library);
|
||||
return loader;
|
||||
}
|
||||
|
||||
Status LibJVMLoader::load() {
|
||||
if (_library.empty()) {
|
||||
return Status::RuntimeError("Failed to find the library %s.", LIBJVM_SO);
|
||||
}
|
||||
|
||||
static std::once_flag resolve_symbols;
|
||||
static Status status;
|
||||
std::call_once(resolve_symbols, [this]() {
|
||||
_handle = std::unique_ptr<void, void (*)(void*)>(dlopen(_library.c_str(), RTLD_LAZY),
|
||||
[](void* handle) { dlclose(handle); });
|
||||
if (!_handle) {
|
||||
status = Status::RuntimeError(dlerror());
|
||||
return;
|
||||
}
|
||||
|
||||
if (status = resolve_symbol(JNI_GetCreatedJavaVMs, _handle.get(), "JNI_GetCreatedJavaVMs");
|
||||
!status.ok()) {
|
||||
return;
|
||||
}
|
||||
if (status = resolve_symbol(JNI_CreateJavaVM, _handle.get(), "JNI_CreateJavaVM");
|
||||
!status.ok()) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
} // namespace doris
|
||||
54
be/src/util/libjvm_loader.h
Normal file
54
be/src/util/libjvm_loader.h
Normal file
@ -0,0 +1,54 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace doris {
|
||||
|
||||
class Status;
|
||||
|
||||
class LibJVMLoader {
|
||||
public:
|
||||
LibJVMLoader(const LibJVMLoader&) = delete;
|
||||
LibJVMLoader& operator=(const LibJVMLoader&) = delete;
|
||||
|
||||
static LibJVMLoader& instance();
|
||||
Status load();
|
||||
|
||||
using JNI_GetCreatedJavaVMsPointer = std::add_pointer_t<decltype(::JNI_GetCreatedJavaVMs)>;
|
||||
static JNI_GetCreatedJavaVMsPointer JNI_GetCreatedJavaVMs;
|
||||
|
||||
using JNI_CreateJavaVMPointer = std::add_pointer_t<decltype(::JNI_CreateJavaVM)>;
|
||||
static JNI_CreateJavaVMPointer JNI_CreateJavaVM;
|
||||
|
||||
private:
|
||||
explicit LibJVMLoader(std::string_view library)
|
||||
: _library(library), _handle(nullptr, nullptr) {}
|
||||
|
||||
const std::string _library;
|
||||
std::unique_ptr<void, void (*)(void*)> _handle;
|
||||
};
|
||||
|
||||
} // namespace doris
|
||||
@ -1,93 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF 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.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
curdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
|
||||
if [[ "$(uname -s)" == 'Darwin' ]] && command -v brew &>/dev/null; then
|
||||
PATH="$(brew --prefix)/opt/gnu-getopt/bin:${PATH}"
|
||||
export PATH
|
||||
fi
|
||||
|
||||
DORIS_HOME="$(
|
||||
cd "${curdir}/.."
|
||||
pwd
|
||||
)"
|
||||
export DORIS_HOME
|
||||
|
||||
jdk_version() {
|
||||
local java_cmd="${1}"
|
||||
local result
|
||||
local IFS=$'\n'
|
||||
|
||||
if [[ -z "${java_cmd}" ]]; then
|
||||
result=no_java
|
||||
return 1
|
||||
else
|
||||
local version
|
||||
# remove \r for Cygwin
|
||||
version="$("${java_cmd}" -Xms32M -Xmx32M -version 2>&1 | tr '\r' '\n' | grep version | awk '{print $3}')"
|
||||
version="${version//\"/}"
|
||||
if [[ "${version}" =~ ^1\. ]]; then
|
||||
result="$(echo "${version}" | awk -F '.' '{print $2}')"
|
||||
else
|
||||
result="$(echo "${version}" | awk -F '.' '{print $1}')"
|
||||
fi
|
||||
fi
|
||||
echo "${result}"
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_java_env() {
|
||||
local java_version
|
||||
|
||||
if [[ -z "${JAVA_HOME}" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local jvm_arch='amd64'
|
||||
if [[ "$(uname -m)" == 'aarch64' ]]; then
|
||||
jvm_arch='aarch64'
|
||||
fi
|
||||
java_version="$(
|
||||
set -e
|
||||
jdk_version "${JAVA_HOME}/bin/java"
|
||||
)"
|
||||
if [[ "${java_version}" -gt 8 ]]; then
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/server:${JAVA_HOME}/lib:${LD_LIBRARY_PATH}"
|
||||
# JAVA_HOME is jdk
|
||||
elif [[ -d "${JAVA_HOME}/jre" ]]; then
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/jre/lib/${jvm_arch}/server:${JAVA_HOME}/jre/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
|
||||
# JAVA_HOME is jre
|
||||
else
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/${jvm_arch}/server:${JAVA_HOME}/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
|
||||
fi
|
||||
}
|
||||
|
||||
# prepare jvm if needed
|
||||
setup_java_env || true
|
||||
|
||||
if [[ -e "${DORIS_HOME}/bin/palo_env.sh" ]]; then
|
||||
# shellcheck disable=1091
|
||||
source "${DORIS_HOME}/bin/palo_env.sh"
|
||||
fi
|
||||
|
||||
chmod 755 "${DORIS_HOME}/lib/doris_be"
|
||||
|
||||
"${DORIS_HOME}"/lib/doris_be --version
|
||||
@ -104,35 +104,6 @@ jdk_version() {
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_java_env() {
|
||||
local java_version
|
||||
|
||||
if [[ -z "${JAVA_HOME}" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local jvm_arch='amd64'
|
||||
if [[ "$(uname -m)" == 'aarch64' ]]; then
|
||||
jvm_arch='aarch64'
|
||||
fi
|
||||
java_version="$(
|
||||
set -e
|
||||
jdk_version "${JAVA_HOME}/bin/java"
|
||||
)"
|
||||
if [[ "${java_version}" -gt 8 ]]; then
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/server:${JAVA_HOME}/lib:${LD_LIBRARY_PATH}"
|
||||
# JAVA_HOME is jdk
|
||||
elif [[ -d "${JAVA_HOME}/jre" ]]; then
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/jre/lib/${jvm_arch}/server:${JAVA_HOME}/jre/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
|
||||
# JAVA_HOME is jre
|
||||
else
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/${jvm_arch}/server:${JAVA_HOME}/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
|
||||
fi
|
||||
}
|
||||
|
||||
# prepare jvm if needed
|
||||
setup_java_env || true
|
||||
|
||||
# export env variables from be.conf
|
||||
#
|
||||
# UDF_RUNTIME_DIR
|
||||
|
||||
1
build.sh
1
build.sh
@ -508,7 +508,6 @@ if [[ "${BUILD_BE}" -eq 1 ]]; then
|
||||
"${DORIS_OUTPUT}/udf/include"
|
||||
|
||||
cp -r -p "${DORIS_HOME}/be/output/bin"/* "${DORIS_OUTPUT}/be/bin"/
|
||||
cp -r -p "${DORIS_HOME}/bin/check_be_version.sh" "${DORIS_OUTPUT}/be/bin"/
|
||||
cp -r -p "${DORIS_HOME}/be/output/conf"/* "${DORIS_OUTPUT}/be/conf"/
|
||||
|
||||
# Fix Killed: 9 error on MacOS (arm64).
|
||||
|
||||
29
run-be-ut.sh
29
run-be-ut.sh
@ -230,35 +230,6 @@ jdk_version() {
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_java_env() {
|
||||
local java_version
|
||||
|
||||
if [[ -z "${JAVA_HOME}" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local jvm_arch='amd64'
|
||||
if [[ "$(uname -m)" == 'aarch64' ]]; then
|
||||
jvm_arch='aarch64'
|
||||
fi
|
||||
java_version="$(
|
||||
set -e
|
||||
jdk_version "${JAVA_HOME}/bin/java"
|
||||
)"
|
||||
if [[ "${java_version}" -gt 8 ]]; then
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/server:${JAVA_HOME}/lib:${LD_LIBRARY_PATH}"
|
||||
# JAVA_HOME is jdk
|
||||
elif [[ -d "${JAVA_HOME}/jre" ]]; then
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/jre/lib/${jvm_arch}/server:${JAVA_HOME}/jre/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
|
||||
# JAVA_HOME is jre
|
||||
else
|
||||
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/${jvm_arch}/server:${JAVA_HOME}/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
|
||||
fi
|
||||
}
|
||||
|
||||
# prepare jvm if needed
|
||||
setup_java_env || true
|
||||
|
||||
# prepare gtest output dir
|
||||
GTEST_OUTPUT_DIR="${CMAKE_BUILD_DIR}/gtest_output"
|
||||
rm -rf "${GTEST_OUTPUT_DIR}"
|
||||
|
||||
Reference in New Issue
Block a user