File pl_sr_jvm.cpp¶
File List > cubrid > src > sp > pl_sr_jvm.cpp
Go to the documentation of this file
/*
*
* Copyright 2016 CUBRID Corporation
*
* Licensed 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.
*
*/
/*
* pl_sr_jvm.cpp - PL Server Module Source related to setup JVM
*/
#include "config.h"
#if defined(WINDOWS)
#include <windows.h>
#define DELAYIMP_INSECURE_WRITABLE_HOOKS
#include <Delayimp.h>
#pragma comment(lib, "delayimp")
#pragma comment(lib, "jvm")
#else /* WINDOWS */
#include <dlfcn.h>
#endif /* !WINDOWS */
#include <jni.h>
#include <locale.h>
#include <assert.h>
#include <vector>
#include <string>
#include <sstream>
#include <iterator>
#include "pl_file.h"
#include "pl_comm.h"
#include "environment_variable.h"
#include "system_parameter.h"
#include "release_string.h"
#include "memory_alloc.h"
#include "error_manager.h"
#if defined(WINDOWS)
#if __WORDSIZE == 32
#define JVM_LIB_PATH_JDK "jre\\bin\\client"
#define JVM_LIB_PATH_JRE "bin\\client"
#define JVM_LIB_PATH_JDK11 "" /* JDK 11 does not support for Windows x64 */
#else
#define JVM_LIB_PATH_JDK "jre\\bin\\server"
#define JVM_LIB_PATH_JRE "bin\\server"
#define JVM_LIB_PATH_JDK11 "bin\\server"
#endif
#else
#define JVM_LIB_PATH "jre/lib/amd64/server"
#define JVM_LIB_PATH_JDK11 "lib/server"
#endif
#if defined(WINDOWS)
#define JVM_LIB_FILE "libjvm.dll"
#else
#define JVM_LIB_FILE "libjvm.so"
#endif
#if defined(WINDOWS)
#define REGKEY_JAVA "Software\\JavaSoft\\Java Runtime Environment"
#endif /* WINDOWS */
#define BUF_SIZE 2048
typedef jint (*CREATE_VM_FUNC) (JavaVM **, void **, void *);
#define JVM_GetEnv(JVM, ENV, VER) \
(JVM)->GetEnv(ENV, VER)
#define JVM_AttachCurrentThread(JVM, ENV, ARGS) \
(JVM)->AttachCurrentThread(ENV, ARGS)
#define JVM_DetachCurrentThread(JVM) \
(JVM)->DetachCurrentThread()
#define JVM_ExceptionOccurred(ENV) \
(ENV)->ExceptionOccurred()
#define JVM_FindClass(ENV, NAME) \
(ENV)->FindClass(NAME)
#define JVM_GetStaticMethodID(ENV, CLAZZ, NAME, SIG) \
(ENV)->GetStaticMethodID(CLAZZ, NAME, SIG)
#define JVM_NewStringUTF(ENV, BYTES) \
(ENV)->NewStringUTF(BYTES);
#define JVM_NewObjectArray(ENV, LENGTH, ELEMENTCLASS, INITIALCLASS) \
(ENV)->NewObjectArray(LENGTH, ELEMENTCLASS, INITIALCLASS)
#define JVM_SetObjectArrayElement(ENV, ARRAY, INDEX, VALUE) \
(ENV)->SetObjectArrayElement(ARRAY, INDEX, VALUE)
#define JVM_CallStaticVoidMethod(ENV, CLAZZ, METHODID, ARGS) \
(ENV)->CallStaticVoidMethod(CLAZZ, METHODID, ARGS)
#define JVM_CallStaticIntMethod(ENV, CLAZZ, METHODID, ARGS) \
(ENV)->CallStaticIntMethod(CLAZZ, METHODID, ARGS)
#define JVM_CallStaticObjectMethod(ENV, CLAZZ, METHODID, ARGS) \
(ENV)->CallStaticObjectMethod(CLAZZ, METHODID, ARGS)
#define JVM_GetStringUTF(ENV, STRING) \
(ENV)->GetStringUTFChars(STRING, NULL)
#define JVM_ReleaseStringUTF(ENV, JSTRING, CSTRING) \
(ENV)->ReleaseStringUTFChars(JSTRING, CSTRING)
#define JVM_GetStringUTFLength(ENV, STRING) \
(ENV)->GetStringUTFLength(STRING)
static JavaVM *jvm = NULL;
static jint sp_port = PL_PORT_DISABLED;
static std::string err_msgs;
#if defined(WINDOWS)
int get_java_root_path (char *path);
static FARPROC WINAPI delay_load_hook (unsigned dliNotify, PDelayLoadInfo pdli);
LONG WINAPI delay_load_dll_exception_filter (PEXCEPTION_POINTERS pep);
PfnDliHook __pfnDliNotifyHook2 = delay_load_hook;
PfnDliHook __pfnDliFailureHook2 = delay_load_hook;
#else /* WINDOWS */
static void *pl_get_create_java_vm_function_ptr (void);
#endif /* !WINDOWS */
#if defined(WINDOWS)
/*
* get_java_root_path()
* return: return FALSE on error othrewise true
* path(in/out): get java root path
*
* Note:
*/
int
get_java_root_path (char *path)
{
DWORD rc;
DWORD len;
DWORD dwType;
char currentVersion[16];
char regkey_java_current_version[BUF_SIZE];
char java_root_path[BUF_SIZE];
HKEY hKeyReg;
if (!path)
{
return false;
}
rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE, REGKEY_JAVA, 0, KEY_QUERY_VALUE, &hKeyReg);
if (rc != ERROR_SUCCESS)
{
return false;
}
len = sizeof (currentVersion);
rc = RegQueryValueEx (hKeyReg, "CurrentVersion", 0, &dwType, (LPBYTE) currentVersion, &len);
if (hKeyReg)
{
RegCloseKey (hKeyReg);
}
if (rc != ERROR_SUCCESS)
{
return false;
}
hKeyReg = NULL;
sprintf (regkey_java_current_version, "%s\\%s", REGKEY_JAVA, currentVersion);
rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE, regkey_java_current_version, 0, KEY_QUERY_VALUE, &hKeyReg);
if (rc != ERROR_SUCCESS)
{
return false;
}
len = sizeof (java_root_path);
rc = RegQueryValueEx (hKeyReg, "JavaHome", 0, &dwType, (LPBYTE) java_root_path, &len);
if (hKeyReg)
{
RegCloseKey (hKeyReg);
}
if (rc != ERROR_SUCCESS)
{
return false;
}
strcpy (path, java_root_path);
return true;
}
/*
* delay_load_hook -
* return:
* dliNotify(in):
* pdli(in):
*
* Note:
*/
static FARPROC WINAPI
delay_load_hook (unsigned dliNotify, PDelayLoadInfo pdli)
{
FARPROC fp = NULL;
switch (dliNotify)
{
case dliFailLoadLib:
{
char java_home_path[PATH_MAX];
char *java_home = NULL, *jvm_path = NULL, *tmp = NULL, *tail;
void *libVM = NULL;
java_home = getenv ("CUBRID_JAVA_HOME");
if (java_home == NULL)
{
envvar_vmdir_file (java_home_path, PATH_MAX, "jdk8");
java_home = java_home_path;
}
tail = JVM_LIB_PATH_JDK;
if (java_home == NULL)
{
tmp = (char *) malloc (BUF_SIZE);
if (tmp)
{
if (get_java_root_path (tmp))
{
java_home = tmp;
tail = JVM_LIB_PATH_JRE;
}
}
}
if (java_home)
{
err_msgs.append ("\n\tFailed to load libjvm from 'CUBRID_JAVA_HOME' environment variable: ");
char jvm_lib_path[BUF_SIZE];
sprintf (jvm_lib_path, "%s\\%s\\jvm.dll", java_home, tail);
err_msgs.append ("\n\t\t");
err_msgs.append (jvm_lib_path);
libVM = LoadLibrary (jvm_lib_path);
if (libVM)
{
fp = (FARPROC) (HMODULE) libVM;
}
else
{
tail = JVM_LIB_PATH_JDK11;
memset (jvm_lib_path, BUF_SIZE, 0);
sprintf (jvm_lib_path, "%s\\%s\\jvm.dll", java_home, tail);
err_msgs.append ("\n\t\t");
err_msgs.append (jvm_lib_path);
libVM = LoadLibrary (jvm_lib_path);
fp = (FARPROC) (HMODULE) libVM;
}
}
else
{
err_msgs.append ("\n\tFailed to get 'CUBRID_JAVA_HOME' environment variable");
}
if (tmp)
{
free_and_init (tmp);
}
}
break;
default:
break;
}
return fp;
}
/*
* delay_load_dll_exception_filter -
* return:
* pep(in):
*
* Note:
*/
LONG WINAPI
delay_load_dll_exception_filter (PEXCEPTION_POINTERS pep)
{
switch (pep->ExceptionRecord->ExceptionCode)
{
case VcppException (ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
case VcppException (ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND):
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_VM_LIB_NOT_FOUND, 1, err_msgs.c_str ());
break;
default:
break;
}
return EXCEPTION_EXECUTE_HANDLER;
}
#else /* WINDOWS */
/*
* pl_get_create_java_vm_func_ptr
* return: return java vm function pointer
*
* Note:
*/
static void *
pl_get_create_java_vm_function_ptr ()
{
void *libVM_p = NULL;
char java_home_path[PATH_MAX];
char *java_home = getenv ("CUBRID_JAVA_HOME");
if (java_home == NULL)
{
envvar_vmdir_file (java_home_path, PATH_MAX, "jdk8");
java_home = java_home_path;
}
if (java_home != NULL)
{
char jvm_library_path[PATH_MAX];
err_msgs.append ("\n\tFailed to load libjvm from 'CUBRID_JAVA_HOME' environment variable: ");
// under jdk 11
if (snprintf (jvm_library_path, PATH_MAX, "%s/%s/%s", java_home, JVM_LIB_PATH, JVM_LIB_FILE) >= PATH_MAX)
{
return NULL;
}
libVM_p = dlopen (jvm_library_path, RTLD_NOW | RTLD_LOCAL);
if (libVM_p != NULL)
{
return dlsym (libVM_p, "JNI_CreateJavaVM");
}
else
{
err_msgs.append ("\n\t\t");
err_msgs.append (dlerror ());
}
// from jdk 11
if (snprintf (jvm_library_path, PATH_MAX, "%s/%s/%s", java_home, JVM_LIB_PATH_JDK11, JVM_LIB_FILE) >= PATH_MAX)
{
return NULL;
}
libVM_p = dlopen (jvm_library_path, RTLD_NOW | RTLD_LOCAL);
if (libVM_p != NULL)
{
return dlsym (libVM_p, "JNI_CreateJavaVM");
}
else
{
err_msgs.append ("\n\t\t");
err_msgs.append (dlerror ());
}
}
else
{
err_msgs.append ("\n\tFailed to get 'CUBRID_JAVA_HOME' environment variable");
}
return NULL;
}
#endif /* !WINDOWS */
/*
* pl_create_java_vm
* return: create java vm
*
* Note:
*/
static int
pl_create_java_vm (JNIEnv **env_p, JavaVMInitArgs *vm_arguments)
{
int res;
#if defined(WINDOWS)
__try
{
res = JNI_CreateJavaVM (&jvm, (void **) env_p, vm_arguments);
}
__except (delay_load_dll_exception_filter (GetExceptionInformation ()))
{
res = -1;
}
#else /* WINDOWS */
CREATE_VM_FUNC create_vm_func = (CREATE_VM_FUNC) pl_get_create_java_vm_function_ptr ();
if (create_vm_func)
{
res = (*create_vm_func) (&jvm, (void **) env_p, (void *) vm_arguments);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_VM_LIB_NOT_FOUND, 1, err_msgs.c_str ());
res = -1;
}
#endif /* WINDOWS */
err_msgs.clear ();
return res;
}
/*
* pl_tokenize_jvm_options
* return: tokenized array of string
*
*/
static std::vector <std::string>
pl_tokenize_jvm_options (char *opt_str)
{
std::string str (opt_str);
std::istringstream iss (str);
std::vector <std::string> options;
std::copy (std::istream_iterator <std::string> (iss),
std::istream_iterator <std::string> (), std::back_inserter (options));
return options;
}
/*
* pl_jvm_options
* return: jvm options
*
*/
static std::vector <std::string>
pl_jvm_options ()
{
char buffer[PATH_MAX];
envvar_vmdir_file (buffer, PATH_MAX, "");
std::string pl_file_path (buffer);
std::vector <std::string> options;
#ifndef NDEBUG
// enable assertions in PL Server
options.push_back ("-ea"); // must be the first option in order not to override ones specified by the user
#endif // !NDEBUG
// CBRD-25659: dump heap memory on JVM OutOfMemory error
if (prm_get_bool_value (PRM_ID_ENABLE_JVM_HEAP_DUMP))
{
envvar_logdir_file (buffer, PATH_MAX, "jvmheapdump.hprof");
std::string jvm_heap_dump_file_path (buffer);
options.push_back ("-XX:+HeapDumpOnOutOfMemoryError");
options.push_back ("-XX:HeapDumpPath=" + jvm_heap_dump_file_path);
}
// defaults
options.push_back ("-Djava.awt.headless=true");
options.push_back ("-Dfile.encoding=UTF-8");
// CBRD-25364: Prevent JVM crash caused by libzip
// Added the following option as a default until the minimum JDK version is upgraded
options.push_back ("-Dsun.zip.disableMemoryMapping=true");
//
options.push_back ("-Djava.class.path=" + pl_file_path + "pl_server.jar");
options.push_back ("-Djava.util.logging.config.file=" + pl_file_path + "logging.properties");
int debug_port = prm_get_integer_value (PRM_ID_JAVA_STORED_PROCEDURE_DEBUG);
if (debug_port != -1)
{
options.push_back ("-Xdebug");
options.push_back ("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" + std::to_string (debug_port));
}
char *jvm_opt_sysprm = (char *) prm_get_string_value (PRM_ID_JAVA_STORED_PROCEDURE_JVM_OPTIONS);
if (jvm_opt_sysprm != NULL)
{
std::vector <std::string> ext_opts = pl_tokenize_jvm_options (jvm_opt_sysprm);
options.insert (options.end(), ext_opts.begin(), ext_opts.end());
}
return options;
}
/*
* pl_start_jvm_server -
* return: Error Code
* db_name(in): db name
* path(in): path
*
* Note:
*/
int
pl_start_jvm_server (const char *db_name, const char *path, int port)
{
jint res;
jclass cls, string_cls;
JNIEnv *env_p = NULL;
jmethodID mid;
jstring jstr_dbname, jstr_path, jstr_version, jstr_envroot, jstr_port, jstr_uds_path;
jobjectArray args;
JavaVMInitArgs vm_arguments;
JavaVMOption *options;
int vm_n_options = 0;
const char *envroot;
const char *uds_path;
char *loc_p, *locale;
const bool is_uds_mode = (port == PL_PORT_UDS_MODE);
{
if (jvm != NULL)
{
return ER_SP_ALREADY_EXIST; /* already created */
}
envroot = envvar_root ();
uds_path = (is_uds_mode) ? pl_get_socket_file_path (db_name) : "";
std::vector <std::string> opts = pl_jvm_options ();
vm_n_options = (int) opts.size ();
options = new JavaVMOption[vm_n_options];
if (options == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 0);
goto exit;
}
int idx = 0;
for (auto it = opts.begin (); it != opts.end (); ++it)
{
options[idx++].optionString = const_cast <char *> (it->c_str ());
}
vm_arguments.version = JNI_VERSION_1_6;
vm_arguments.nOptions = vm_n_options;
vm_arguments.options = options;
vm_arguments.ignoreUnrecognized = JNI_TRUE;
locale = NULL;
loc_p = setlocale (LC_TIME, NULL);
if (loc_p != NULL)
{
locale = strdup (loc_p);
}
res = pl_create_java_vm (&env_p, &vm_arguments);
delete[] options;
if (er_has_error ())
{
if (locale != NULL)
{
free (locale);
}
return er_errid ();
}
setlocale (LC_TIME, locale);
if (locale != NULL)
{
free (locale);
}
if (res < 0)
{
jvm = NULL;
return er_errid ();
}
cls = JVM_FindClass (env_p, "com/cubrid/jsp/Server");
if (cls == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1, "FindClass: " "com/cubrid/jsp/Server");
goto exit;
}
mid = JVM_GetStaticMethodID (env_p, cls, "main", "([Ljava/lang/String;)V");
if (mid == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"GetStaticMethodID: " "com/cubrid/jsp/Server.main([Ljava/lang/String;)V");
goto exit;
}
jstr_dbname = JVM_NewStringUTF (env_p, db_name);
if (jstr_dbname == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"Failed to construct a new 'java.lang.String object' by NewStringUTF()");
goto exit;
}
jstr_path = JVM_NewStringUTF (env_p, path);
if (jstr_path == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"Failed to construct a new 'java.lang.String object' by NewStringUTF()");
goto exit;
}
jstr_version = JVM_NewStringUTF (env_p, rel_build_number ());
if (jstr_version == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"Failed to construct a new 'java.lang.String object' by NewStringUTF()");
goto exit;
}
jstr_envroot = JVM_NewStringUTF (env_p, envroot);
if (jstr_envroot == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"Failed to construct a new 'java.lang.String object' by NewStringUTF()");
goto exit;
}
jstr_uds_path = JVM_NewStringUTF (env_p, uds_path);
if (jstr_uds_path == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"Failed to construct a new 'java.lang.String object' by NewStringUTF()");
goto exit;
}
char port_str[6] = { 0 };
sprintf (port_str, "%d", port);
jstr_port = JVM_NewStringUTF (env_p, port_str);
if (jstr_port == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"Failed to construct a new 'java.lang.String object' by NewStringUTF()");
goto exit;
}
string_cls = JVM_FindClass (env_p, "java/lang/String");
if (string_cls == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1, "FindClass: " "java/lang/String");
goto exit;
}
args = JVM_NewObjectArray (env_p, 6, string_cls, NULL);
if (args == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"Failed to construct a new java array by NewObjectArray()");
goto exit;
}
JVM_SetObjectArrayElement (env_p, args, 0, jstr_dbname);
JVM_SetObjectArrayElement (env_p, args, 1, jstr_path);
JVM_SetObjectArrayElement (env_p, args, 2, jstr_version);
JVM_SetObjectArrayElement (env_p, args, 3, jstr_envroot);
JVM_SetObjectArrayElement (env_p, args, 4, jstr_uds_path);
JVM_SetObjectArrayElement (env_p, args, 5, jstr_port);
sp_port = JVM_CallStaticIntMethod (env_p, cls, mid, args);
if (JVM_ExceptionOccurred (env_p))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_START_PL_SERVER, 1,
"Error occured while starting Java SP Server by CallStaticIntMethod()");
goto exit;
}
}
exit:
#if defined (SA_MODE)
if (jvm != NULL)
{
JVM_DetachCurrentThread (jvm);
}
#endif
return er_errid ();
}
int
pl_server_port (void)
{
return sp_port;
}