summaryrefslogtreecommitdiffstats
path: root/storage/connect/javaconn.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/connect/javaconn.cpp')
-rw-r--r--storage/connect/javaconn.cpp618
1 files changed, 618 insertions, 0 deletions
diff --git a/storage/connect/javaconn.cpp b/storage/connect/javaconn.cpp
new file mode 100644
index 00000000..847014e3
--- /dev/null
+++ b/storage/connect/javaconn.cpp
@@ -0,0 +1,618 @@
+/************ Javaconn C++ Functions Source Code File (.CPP) ***********/
+/* Name: JAVAConn.CPP Version 1.1 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2017 - 2021 */
+/* */
+/* This file contains the JAVA connection classes functions. */
+/***********************************************************************/
+
+#if defined(_WIN32)
+// This is needed for RegGetValue
+#define _WINVER 0x0601
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0601
+#endif // _WIN32
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include <my_global.h>
+//#include <m_string.h>
+#if defined(_WIN32)
+#include <direct.h> // for getcwd
+#if defined(__BORLANDC__)
+#define __MFC_COMPAT__ // To define min/max as macro
+#endif // __BORLANDC__
+#else // !_WIN32
+#if defined(UNIX)
+#include <errno.h>
+#else // !UNIX
+#endif // !UNIX
+#include <stdio.h>
+#include <stdlib.h> // for getenv
+#define NODW
+#endif // !_WIN32
+
+#include <m_string.h>
+
+/***********************************************************************/
+/* Required objects includes. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "colblk.h"
+#include "xobject.h"
+#include "xtable.h"
+#include "tabext.h"
+#include "javaconn.h"
+#include "resource.h"
+#include "valblk.h"
+#include "osutil.h"
+
+#if defined(_WIN32)
+extern "C" HINSTANCE s_hModule; // Saved module handle
+#endif // _WIN32
+#define nullptr 0
+
+//TYPCONV GetTypeConv();
+//int GetConvSize();
+extern char *JvmPath; // The connect_jvm_path global variable value
+extern char *ClassPath; // The connect_class_path global variable value
+
+char *GetPluginDir(void);
+char *GetMessageDir(void);
+char *GetJavaWrapper(void); // The connect_java_wrapper variable value
+extern MYSQL_PLUGIN_IMPORT char lc_messages_dir[FN_REFLEN];
+
+/***********************************************************************/
+/* Static JAVAConn objects. */
+/***********************************************************************/
+void *JAVAConn::LibJvm = NULL;
+CRTJVM JAVAConn::CreateJavaVM = NULL;
+GETJVM JAVAConn::GetCreatedJavaVMs = NULL;
+#if defined(_DEBUG)
+GETDEF JAVAConn::GetDefaultJavaVMInitArgs = NULL;
+#endif // _DEBUG
+
+/***********************************************************************/
+/* Some macro's (should be defined elsewhere to be more accessible) */
+/***********************************************************************/
+#if defined(_DEBUG)
+#define ASSERT(f) assert(f)
+#define DEBUG_ONLY(f) (f)
+#else // !_DEBUG
+#define ASSERT(f) ((void)0)
+#define DEBUG_ONLY(f) ((void)0)
+#endif // !_DEBUG
+
+/***********************************************************************/
+/* JAVAConn construction/destruction. */
+/***********************************************************************/
+JAVAConn::JAVAConn(PGLOBAL g, PCSZ wrapper)
+{
+ m_G = g;
+ jvm = nullptr; // Pointer to the JVM (Java Virtual Machine)
+ env = nullptr; // Pointer to native interface
+ jdi = nullptr; // Pointer to the java wrapper class
+ job = nullptr; // The java wrapper class object
+ errid = nullptr;
+ DiscFunc = "Disconnect";
+ Msg = NULL;
+ m_Wrap = (wrapper) ? wrapper : GetJavaWrapper();
+
+ if (!strchr(m_Wrap, '/')) {
+ // Add the wrapper package name
+ char *wn = (char*)PlugSubAlloc(g, NULL, strlen(m_Wrap) + 10);
+ m_Wrap = strcat(strcpy(wn, "wrappers/"), m_Wrap);
+ } // endif m_Wrap
+
+ fp = NULL;
+ m_Opened = false;
+ m_Connected = false;
+ m_Rows = 0;
+//*m_ErrMsg = '\0';
+} // end of JAVAConn
+
+//JAVAConn::~JAVAConn()
+// {
+//if (Connected())
+// EndCom();
+
+// } // end of ~JAVAConn
+char *JAVAConn::GetUTFString(jstring s)
+{
+ char *str;
+ const char *utf = env->GetStringUTFChars(s, nullptr);
+
+ str = PlugDup(m_G, utf);
+ env->ReleaseStringUTFChars(s, utf);
+ env->DeleteLocalRef(s);
+ return str;
+} // end of GetUTFString
+
+/***********************************************************************/
+/* Screen for errors. */
+/***********************************************************************/
+bool JAVAConn::Check(jint rc)
+{
+ jstring s;
+
+ if (env->ExceptionCheck()) {
+ jthrowable exc = env->ExceptionOccurred();
+ jmethodID tid = env->GetMethodID(env->FindClass("java/lang/Object"),
+ "toString", "()Ljava/lang/String;");
+
+ if (exc != nullptr && tid != nullptr) {
+ s = (jstring)env->CallObjectMethod(exc, tid);
+ Msg = GetUTFString(s);
+ } else
+ Msg = "Exception occurred";
+
+ env->ExceptionClear();
+ } else if (rc < 0) {
+ s = (jstring)env->CallObjectMethod(job, errid);
+ Msg = GetUTFString(s);
+ } else
+ Msg = NULL;
+
+ return (Msg != NULL);
+} // end of Check
+
+/***********************************************************************/
+/* Get MethodID if not exists yet. */
+/***********************************************************************/
+bool JAVAConn::gmID(PGLOBAL g, jmethodID& mid, const char *name, const char *sig)
+{
+ if (mid == nullptr) {
+ mid = env->GetMethodID(jdi, name, sig);
+
+ if (Check()) {
+ snprintf(g->Message, sizeof(g->Message), "%s", Msg);
+ return true;
+ } else
+ return false;
+
+ } else
+ return false;
+
+} // end of gmID
+
+#if 0
+/***********************************************************************/
+/* Utility routine. */
+/***********************************************************************/
+int JAVAConn::GetMaxValue(int n)
+{
+ jint m;
+ jmethodID maxid = nullptr;
+
+ if (gmID(m_G, maxid, "GetMaxValue", "(I)I"))
+ return -1;
+
+ // call method
+ if (Check(m = env->CallIntMethod(job, maxid, n)))
+ htrc("GetMaxValue: %s", Msg);
+
+ return (int)m;
+} // end of GetMaxValue
+#endif // 0
+
+/***********************************************************************/
+/* Reset the JVM library. */
+/***********************************************************************/
+void JAVAConn::ResetJVM(void)
+{
+ if (LibJvm) {
+#if defined(_WIN32)
+ FreeLibrary((HMODULE)LibJvm);
+#else // !_WIN32
+ dlclose(LibJvm);
+#endif // !_WIN32
+ LibJvm = NULL;
+ CreateJavaVM = NULL;
+ GetCreatedJavaVMs = NULL;
+#if defined(_DEBUG)
+ GetDefaultJavaVMInitArgs = NULL;
+#endif // _DEBUG
+ } // endif LibJvm
+
+} // end of ResetJVM
+
+/***********************************************************************/
+/* Dynamically link the JVM library. */
+/* The purpose of this function is to allow using the CONNECT plugin */
+/* for other table types when the Java JDK is not installed. */
+/***********************************************************************/
+bool JAVAConn::GetJVM(PGLOBAL g)
+{
+ int ntry;
+
+ if (!LibJvm) {
+ char soname[512];
+
+#if defined(_WIN32)
+ for (ntry = 0; !LibJvm && ntry < 3; ntry++) {
+ if (!ntry && JvmPath) {
+ snprintf(soname, sizeof(soname), "%s\\jvm.dll", JvmPath);
+
+ ntry = 3; // No other try
+ } else if (ntry < 2 && getenv("JAVA_HOME")) {
+ safe_strcpy(soname, sizeof(soname), getenv("JAVA_HOME"));
+
+ if (ntry == 1)
+ safe_strcat(soname, sizeof(soname), "\\jre");
+
+ safe_strcat(soname, sizeof(soname), "\\bin\\client\\jvm.dll");
+ } else {
+ // Try to find it through the registry
+ char version[16];
+ char javaKey[64] = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
+ LONG rc;
+ DWORD BufferSize = 16;
+
+ safe_strcpy(soname, sizeof(soname), "jvm.dll"); // In case it fails
+
+ if ((rc = RegGetValue(HKEY_LOCAL_MACHINE, javaKey, "CurrentVersion",
+ RRF_RT_ANY, NULL, (PVOID)&version, &BufferSize)) == ERROR_SUCCESS) {
+ safe_strcat(javaKey, sizeof(javaKey), "\\");
+ safe_strcat(javaKey, sizeof(javaKey), version);
+ BufferSize = sizeof(soname);
+
+ if ((rc = RegGetValue(HKEY_LOCAL_MACHINE, javaKey, "RuntimeLib",
+ RRF_RT_ANY, NULL, (PVOID)&soname, &BufferSize)) != ERROR_SUCCESS)
+ printf("RegGetValue: rc=%ld\n", rc);
+
+ } // endif rc
+
+ ntry = 3; // Try this only once
+ } // endelse
+
+ // Load the desired shared library
+ LibJvm = LoadLibrary(soname);
+ } // endfor ntry
+
+ // Get the needed entries
+ if (!LibJvm) {
+ char buf[256];
+ DWORD rc = GetLastError();
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
+ (LPTSTR)buf, sizeof(buf), NULL);
+ snprintf(g->Message, sizeof(g->Message), MSG(DLL_LOAD_ERROR)": %s", rc,
+ soname, buf);
+ } else if (!(CreateJavaVM = (CRTJVM)GetProcAddress((HINSTANCE)LibJvm,
+ "JNI_CreateJavaVM"))) {
+ snprintf(g->Message, sizeof(g->Message), MSG(PROCADD_ERROR), GetLastError(), "JNI_CreateJavaVM");
+ FreeLibrary((HMODULE)LibJvm);
+ LibJvm = NULL;
+ } else if (!(GetCreatedJavaVMs = (GETJVM)GetProcAddress((HINSTANCE)LibJvm,
+ "JNI_GetCreatedJavaVMs"))) {
+ snprintf(g->Message, sizeof(g->Message), MSG(PROCADD_ERROR), GetLastError(), "JNI_GetCreatedJavaVMs");
+ FreeLibrary((HMODULE)LibJvm);
+ LibJvm = NULL;
+#if defined(_DEBUG)
+ } else if (!(GetDefaultJavaVMInitArgs = (GETDEF)GetProcAddress((HINSTANCE)LibJvm,
+ "JNI_GetDefaultJavaVMInitArgs"))) {
+ snprintf(g->Message, sizeof(g->Message), MSG(PROCADD_ERROR), GetLastError(),
+ "JNI_GetDefaultJavaVMInitArgs");
+ FreeLibrary((HMODULE)LibJvm);
+ LibJvm = NULL;
+#endif // _DEBUG
+ } // endif LibJvm
+#else // !_WIN32
+ const char *error = NULL;
+
+ for (ntry = 0; !LibJvm && ntry < 2; ntry++) {
+ if (!ntry && JvmPath) {
+ snprintf(soname, sizeof(soname), "%s/libjvm.so", JvmPath);
+ ntry = 2;
+ } else if (!ntry && getenv("JAVA_HOME")) {
+ // TODO: Replace i386 by a better guess
+ snprintf(soname, sizeof(soname), "%s/jre/lib/i386/client/libjvm.so",
+ getenv("JAVA_HOME"));
+ } else { // Will need LD_LIBRARY_PATH to be set
+ safe_strcpy(soname, sizeof(soname), "libjvm.so");
+ ntry = 2;
+ } // endelse
+
+ LibJvm = dlopen(soname, RTLD_LAZY);
+ } // endfor ntry
+
+ // Load the desired shared library
+ if (!LibJvm) {
+ error = dlerror();
+ snprintf(g->Message, sizeof(g->Message), MSG(SHARED_LIB_ERR), soname, SVP(error));
+ } else if (!(CreateJavaVM = (CRTJVM)dlsym(LibJvm, "JNI_CreateJavaVM"))) {
+ error = dlerror();
+ snprintf(g->Message, sizeof(g->Message), MSG(GET_FUNC_ERR), "JNI_CreateJavaVM", SVP(error));
+ dlclose(LibJvm);
+ LibJvm = NULL;
+ } else if (!(GetCreatedJavaVMs = (GETJVM)dlsym(LibJvm, "JNI_GetCreatedJavaVMs"))) {
+ error = dlerror();
+ snprintf(g->Message, sizeof(g->Message), MSG(GET_FUNC_ERR), "JNI_GetCreatedJavaVMs", SVP(error));
+ dlclose(LibJvm);
+ LibJvm = NULL;
+#if defined(_DEBUG)
+ } else if (!(GetDefaultJavaVMInitArgs = (GETDEF)dlsym(LibJvm,
+ "JNI_GetDefaultJavaVMInitArgs"))) {
+ error = dlerror();
+ snprintf(g->Message, sizeof(g->Message), MSG(GET_FUNC_ERR), "JNI_GetDefaultJavaVMInitArgs", SVP(error));
+ dlclose(LibJvm);
+ LibJvm = NULL;
+#endif // _DEBUG
+ } // endif LibJvm
+#endif // !_WIN32
+
+ } // endif LibJvm
+
+ return LibJvm == NULL;
+} // end of GetJVM
+
+/***********************************************************************/
+/* Open: connect to a data source. */
+/***********************************************************************/
+bool JAVAConn::Open(PGLOBAL g)
+{
+ bool brc = true;
+ jboolean jt = (trace(1));
+
+ // Link or check whether jvm library was linked
+ if (GetJVM(g))
+ return true;
+
+ // Firstly check whether the jvm was already created
+ JavaVM* jvms[1];
+ jsize jsz;
+ jint rc = GetCreatedJavaVMs(jvms, 1, &jsz);
+
+ if (rc == JNI_OK && jsz == 1) {
+ // jvm already existing
+ jvm = jvms[0];
+ rc = jvm->AttachCurrentThread((void**)&env, nullptr);
+
+ if (rc != JNI_OK) {
+ snprintf(g->Message, sizeof(g->Message), "Cannot attach jvm to the current thread");
+ return true;
+ } // endif rc
+
+ } else {
+ /*******************************************************************/
+ /* Create a new jvm */
+ /*******************************************************************/
+ PSTRG jpop = new(g)STRING(g, 512, "-Djava.class.path=.");
+ char *cp = NULL;
+ char sep;
+
+#if defined(_WIN32)
+ sep = ';';
+#define N 1
+ //#define N 2
+ //#define N 3
+#else
+ sep = ':';
+#define N 1
+#endif
+
+ // Add wrappers jar files
+ AddJars(jpop, sep);
+
+ //================== prepare loading of Java VM ============================
+ JavaVMInitArgs vm_args; // Initialization arguments
+ JavaVMOption* options = new JavaVMOption[N]; // JVM invocation options
+
+ // where to find java .class
+ if (ClassPath && *ClassPath) {
+ jpop->Append(sep);
+ jpop->Append(ClassPath);
+ } // endif ClassPath
+
+ // All wrappers are pre-compiled in JavaWrappers.jar in the share dir
+ jpop->Append(sep);
+ jpop->Append(GetMessageDir());
+ jpop->Append("JavaWrappers.jar");
+
+#if defined(MONGO_SUPPORT)
+ jpop->Append(sep);
+ jpop->Append(GetMessageDir());
+ jpop->Append("Mongo3.jar");
+ jpop->Append(sep);
+ jpop->Append(GetMessageDir());
+ jpop->Append("Mongo2.jar");
+#endif // MONGO_SUPPORT
+
+ if ((cp = getenv("CLASSPATH"))) {
+ jpop->Append(sep);
+ jpop->Append(cp);
+ } // endif cp
+
+ if (trace(1)) {
+ htrc("ClassPath=%s\n", ClassPath ? ClassPath : "null");
+ htrc("CLASSPATH=%s\n", cp ? cp : "null");
+ htrc("%s\n", jpop->GetStr());
+ } // endif trace
+
+ options[0].optionString = jpop->GetStr();
+#if N == 2
+ options[1].optionString = "-Xcheck:jni";
+#endif
+#if N == 3
+ options[1].optionString = "-Xms256M";
+ options[2].optionString = "-Xmx512M";
+#endif
+#if defined(_DEBUG)
+ vm_args.version = JNI_VERSION_1_2; // minimum Java version
+ rc = GetDefaultJavaVMInitArgs(&vm_args);
+#else
+ vm_args.version = JNI_VERSION_1_6; // minimum Java version
+#endif // _DEBUG
+ vm_args.nOptions = N; // number of options
+ vm_args.options = options;
+ vm_args.ignoreUnrecognized = false; // invalid options make the JVM init fail
+
+ //=============== load and initialize Java VM and JNI interface =============
+ rc = CreateJavaVM(&jvm, (void**)&env, &vm_args); // YES !!
+ delete[] options; // we then no longer need the initialisation options.
+
+ switch (rc) {
+ case JNI_OK:
+ snprintf(g->Message, sizeof(g->Message), "VM successfully created");
+ brc = false;
+ break;
+ case JNI_ERR:
+ snprintf(g->Message, sizeof(g->Message), "Initialising JVM failed: unknown error");
+ break;
+ case JNI_EDETACHED:
+ snprintf(g->Message, sizeof(g->Message), "Thread detached from the VM");
+ break;
+ case JNI_EVERSION:
+ snprintf(g->Message, sizeof(g->Message), "JNI version error");
+ break;
+ case JNI_ENOMEM:
+ snprintf(g->Message, sizeof(g->Message), "Not enough memory");
+ break;
+ case JNI_EEXIST:
+ snprintf(g->Message, sizeof(g->Message), "VM already created");
+ break;
+ case JNI_EINVAL:
+ snprintf(g->Message, sizeof(g->Message), "Invalid arguments");
+ break;
+ default:
+ snprintf(g->Message, sizeof(g->Message), "Unknown return code %d", (int)rc);
+ break;
+ } // endswitch rc
+
+ if (trace(1))
+ htrc("%s\n", g->Message);
+
+ if (brc)
+ return true;
+
+ //=============== Display JVM version ===============
+ jint ver = env->GetVersion();
+ printf("JVM Version %d.%d\n", ((ver >> 16) & 0x0f), (ver & 0x0f));
+ } // endif rc
+
+ // try to find the java wrapper class
+ jdi = env->FindClass(m_Wrap);
+
+ if (jdi == nullptr) {
+ snprintf(g->Message, sizeof(g->Message), "ERROR: class %s not found!", m_Wrap);
+ return true;
+ } // endif jdi
+
+#if 0 // Suppressed because it does not make any usable change
+ if (b && jpath && *jpath) {
+ // Try to add that path the the jvm class path
+ jmethodID alp = env->GetStaticMethodID(jdi, "addLibraryPath",
+ "(Ljava/lang/String;)I");
+
+ if (alp == nullptr) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ } else {
+ char *msg;
+ jstring path = env->NewStringUTF(jpath);
+ rc = env->CallStaticIntMethod(jdi, alp, path);
+
+ if ((msg = Check(rc))) {
+ snprintf(g->Message, sizeof(g->Message), msg);
+ env->DeleteLocalRef(path);
+ return RC_FX;
+ } else switch (rc) {
+ case JNI_OK:
+ printf("jpath added\n");
+ break;
+ case JNI_EEXIST:
+ printf("jpath already exist\n");
+ break;
+ case JNI_ERR:
+ default:
+ snprintf(g->Message, sizeof(g->Message), "Error adding jpath");
+ env->DeleteLocalRef(path);
+ return RC_FX;
+ } // endswitch rc
+
+ env->DeleteLocalRef(path);
+ } // endif alp
+
+ } // endif jpath
+#endif // 0
+
+ // if class found, continue
+ jmethodID ctor = env->GetMethodID(jdi, "<init>", "(Z)V");
+
+ if (ctor == nullptr) {
+ snprintf(g->Message, sizeof(g->Message), "ERROR: %s constructor not found!", m_Wrap);
+ return true;
+ } else
+ job = env->NewObject(jdi, ctor, jt);
+
+ if (job == nullptr) {
+ snprintf(g->Message, sizeof(g->Message), "%s class object not constructed!", m_Wrap);
+ return true;
+ } // endif job
+
+ // If the object is successfully constructed,
+ // we can then search for the method we want to call,
+ // and invoke it for the object:
+ errid = env->GetMethodID(jdi, "GetErrmsg", "()Ljava/lang/String;");
+
+ if (env->ExceptionCheck()) {
+ snprintf(g->Message, sizeof(g->Message), "ERROR: method GetErrmsg() not found!");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return true;
+ } // endif Check
+
+ /*********************************************************************/
+ /* Link a Fblock. This make possible to automatically close it */
+ /* in case of error (throw). */
+ /*********************************************************************/
+ PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
+
+ fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
+ fp->Type = TYPE_FB_JAVA;
+ fp->Fname = NULL;
+ fp->Next = dbuserp->Openlist;
+ dbuserp->Openlist = fp;
+ fp->Count = 1;
+ fp->Length = 0;
+ fp->Memory = NULL;
+ fp->Mode = MODE_ANY;
+ fp->File = this;
+ fp->Handle = 0;
+
+ m_Opened = true;
+ return false;
+} // end of Open
+
+/***********************************************************************/
+/* Disconnect connection */
+/***********************************************************************/
+void JAVAConn::Close()
+{
+ jint rc;
+
+ if (m_Connected) {
+ jmethodID did = nullptr;
+
+ // Could have been detached in case of join
+ rc = jvm->AttachCurrentThread((void**)&env, nullptr);
+
+ if (gmID(m_G, did, DiscFunc, "()I"))
+ printf("%s\n", Msg);
+ else if (Check(env->CallIntMethod(job, did)))
+ printf("%s: %s\n", DiscFunc, Msg);
+
+ m_Connected = false;
+ } // endif m_Connected
+
+ if ((rc = jvm->DetachCurrentThread()) != JNI_OK)
+ printf("DetachCurrentThread: rc=%d\n", (int)rc);
+
+ if (fp)
+ fp->Count = 0;
+
+ m_Opened = false;
+} // end of Close