1 /************ Javaconn C++ Functions Source Code File (.CPP) ***********/
2 /* Name: JAVAConn.CPP Version 1.1 */
3 /* */
4 /* (C) Copyright to the author Olivier BERTRAND 2017 - 2021 */
5 /* */
6 /* This file contains the JAVA connection classes functions. */
7 /***********************************************************************/
8
9 #if defined(_WIN32)
10 // This is needed for RegGetValue
11 #define _WINVER 0x0601
12 #undef _WIN32_WINNT
13 #define _WIN32_WINNT 0x0601
14 #endif // _WIN32
15
16 /***********************************************************************/
17 /* Include relevant MariaDB header file. */
18 /***********************************************************************/
19 #include <my_global.h>
20 //#include <m_string.h>
21 #if defined(_WIN32)
22 #include <direct.h> // for getcwd
23 #if defined(__BORLANDC__)
24 #define __MFC_COMPAT__ // To define min/max as macro
25 #endif // __BORLANDC__
26 #else // !_WIN32
27 #if defined(UNIX)
28 #include <errno.h>
29 #else // !UNIX
30 #endif // !UNIX
31 #include <stdio.h>
32 #include <stdlib.h> // for getenv
33 #define NODW
34 #endif // !_WIN32
35
36 /***********************************************************************/
37 /* Required objects includes. */
38 /***********************************************************************/
39 #include "global.h"
40 #include "plgdbsem.h"
41 #include "colblk.h"
42 #include "xobject.h"
43 #include "xtable.h"
44 #include "tabext.h"
45 #include "javaconn.h"
46 #include "resource.h"
47 #include "valblk.h"
48 #include "osutil.h"
49
50 #if defined(_WIN32)
51 extern "C" HINSTANCE s_hModule; // Saved module handle
52 #endif // _WIN32
53 #define nullptr 0
54
55 //TYPCONV GetTypeConv();
56 //int GetConvSize();
57 extern char *JvmPath; // The connect_jvm_path global variable value
58 extern char *ClassPath; // The connect_class_path global variable value
59
60 char *GetPluginDir(void);
61 char *GetMessageDir(void);
62 char *GetJavaWrapper(void); // The connect_java_wrapper variable value
63 extern MYSQL_PLUGIN_IMPORT char lc_messages_dir[FN_REFLEN];
64
65 /***********************************************************************/
66 /* Static JAVAConn objects. */
67 /***********************************************************************/
68 void *JAVAConn::LibJvm = NULL;
69 CRTJVM JAVAConn::CreateJavaVM = NULL;
70 GETJVM JAVAConn::GetCreatedJavaVMs = NULL;
71 #if defined(_DEBUG)
72 GETDEF JAVAConn::GetDefaultJavaVMInitArgs = NULL;
73 #endif // _DEBUG
74
75 /***********************************************************************/
76 /* Some macro's (should be defined elsewhere to be more accessible) */
77 /***********************************************************************/
78 #if defined(_DEBUG)
79 #define ASSERT(f) assert(f)
80 #define DEBUG_ONLY(f) (f)
81 #else // !_DEBUG
82 #define ASSERT(f) ((void)0)
83 #define DEBUG_ONLY(f) ((void)0)
84 #endif // !_DEBUG
85
86 /***********************************************************************/
87 /* JAVAConn construction/destruction. */
88 /***********************************************************************/
JAVAConn(PGLOBAL g,PCSZ wrapper)89 JAVAConn::JAVAConn(PGLOBAL g, PCSZ wrapper)
90 {
91 m_G = g;
92 jvm = nullptr; // Pointer to the JVM (Java Virtual Machine)
93 env = nullptr; // Pointer to native interface
94 jdi = nullptr; // Pointer to the java wrapper class
95 job = nullptr; // The java wrapper class object
96 errid = nullptr;
97 DiscFunc = "Disconnect";
98 Msg = NULL;
99 m_Wrap = (wrapper) ? wrapper : GetJavaWrapper();
100
101 if (!strchr(m_Wrap, '/')) {
102 // Add the wrapper package name
103 char *wn = (char*)PlugSubAlloc(g, NULL, strlen(m_Wrap) + 10);
104 m_Wrap = strcat(strcpy(wn, "wrappers/"), m_Wrap);
105 } // endif m_Wrap
106
107 fp = NULL;
108 m_Opened = false;
109 m_Connected = false;
110 m_Rows = 0;
111 //*m_ErrMsg = '\0';
112 } // end of JAVAConn
113
114 //JAVAConn::~JAVAConn()
115 // {
116 //if (Connected())
117 // EndCom();
118
119 // } // end of ~JAVAConn
GetUTFString(jstring s)120 char *JAVAConn::GetUTFString(jstring s)
121 {
122 char *str;
123 const char *utf = env->GetStringUTFChars(s, nullptr);
124
125 str = PlugDup(m_G, utf);
126 env->ReleaseStringUTFChars(s, utf);
127 env->DeleteLocalRef(s);
128 return str;
129 } // end of GetUTFString
130
131 /***********************************************************************/
132 /* Screen for errors. */
133 /***********************************************************************/
Check(jint rc)134 bool JAVAConn::Check(jint rc)
135 {
136 jstring s;
137
138 if (env->ExceptionCheck()) {
139 jthrowable exc = env->ExceptionOccurred();
140 jmethodID tid = env->GetMethodID(env->FindClass("java/lang/Object"),
141 "toString", "()Ljava/lang/String;");
142
143 if (exc != nullptr && tid != nullptr) {
144 s = (jstring)env->CallObjectMethod(exc, tid);
145 Msg = GetUTFString(s);
146 } else
147 Msg = "Exception occurred";
148
149 env->ExceptionClear();
150 } else if (rc < 0) {
151 s = (jstring)env->CallObjectMethod(job, errid);
152 Msg = GetUTFString(s);
153 } else
154 Msg = NULL;
155
156 return (Msg != NULL);
157 } // end of Check
158
159 /***********************************************************************/
160 /* Get MethodID if not exists yet. */
161 /***********************************************************************/
gmID(PGLOBAL g,jmethodID & mid,const char * name,const char * sig)162 bool JAVAConn::gmID(PGLOBAL g, jmethodID& mid, const char *name, const char *sig)
163 {
164 if (mid == nullptr) {
165 mid = env->GetMethodID(jdi, name, sig);
166
167 if (Check()) {
168 strcpy(g->Message, Msg);
169 return true;
170 } else
171 return false;
172
173 } else
174 return false;
175
176 } // end of gmID
177
178 #if 0
179 /***********************************************************************/
180 /* Utility routine. */
181 /***********************************************************************/
182 int JAVAConn::GetMaxValue(int n)
183 {
184 jint m;
185 jmethodID maxid = nullptr;
186
187 if (gmID(m_G, maxid, "GetMaxValue", "(I)I"))
188 return -1;
189
190 // call method
191 if (Check(m = env->CallIntMethod(job, maxid, n)))
192 htrc("GetMaxValue: %s", Msg);
193
194 return (int)m;
195 } // end of GetMaxValue
196 #endif // 0
197
198 /***********************************************************************/
199 /* Reset the JVM library. */
200 /***********************************************************************/
ResetJVM(void)201 void JAVAConn::ResetJVM(void)
202 {
203 if (LibJvm) {
204 #if defined(_WIN32)
205 FreeLibrary((HMODULE)LibJvm);
206 #else // !_WIN32
207 dlclose(LibJvm);
208 #endif // !_WIN32
209 LibJvm = NULL;
210 CreateJavaVM = NULL;
211 GetCreatedJavaVMs = NULL;
212 #if defined(_DEBUG)
213 GetDefaultJavaVMInitArgs = NULL;
214 #endif // _DEBUG
215 } // endif LibJvm
216
217 } // end of ResetJVM
218
219 /***********************************************************************/
220 /* Dynamically link the JVM library. */
221 /* The purpose of this function is to allow using the CONNECT plugin */
222 /* for other table types when the Java JDK is not installed. */
223 /***********************************************************************/
GetJVM(PGLOBAL g)224 bool JAVAConn::GetJVM(PGLOBAL g)
225 {
226 int ntry;
227
228 if (!LibJvm) {
229 char soname[512];
230
231 #if defined(_WIN32)
232 for (ntry = 0; !LibJvm && ntry < 3; ntry++) {
233 if (!ntry && JvmPath) {
234 strcat(strcpy(soname, JvmPath), "\\jvm.dll");
235 ntry = 3; // No other try
236 } else if (ntry < 2 && getenv("JAVA_HOME")) {
237 strcpy(soname, getenv("JAVA_HOME"));
238
239 if (ntry == 1)
240 strcat(soname, "\\jre");
241
242 strcat(soname, "\\bin\\client\\jvm.dll");
243 } else {
244 // Try to find it through the registry
245 char version[16];
246 char javaKey[64] = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
247 LONG rc;
248 DWORD BufferSize = 16;
249
250 strcpy(soname, "jvm.dll"); // In case it fails
251
252 if ((rc = RegGetValue(HKEY_LOCAL_MACHINE, javaKey, "CurrentVersion",
253 RRF_RT_ANY, NULL, (PVOID)&version, &BufferSize)) == ERROR_SUCCESS) {
254 strcat(strcat(javaKey, "\\"), version);
255 BufferSize = sizeof(soname);
256
257 if ((rc = RegGetValue(HKEY_LOCAL_MACHINE, javaKey, "RuntimeLib",
258 RRF_RT_ANY, NULL, (PVOID)&soname, &BufferSize)) != ERROR_SUCCESS)
259 printf("RegGetValue: rc=%ld\n", rc);
260
261 } // endif rc
262
263 ntry = 3; // Try this only once
264 } // endelse
265
266 // Load the desired shared library
267 LibJvm = LoadLibrary(soname);
268 } // endfor ntry
269
270 // Get the needed entries
271 if (!LibJvm) {
272 char buf[256];
273 DWORD rc = GetLastError();
274
275 sprintf(g->Message, MSG(DLL_LOAD_ERROR), rc, soname);
276 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
277 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
278 (LPTSTR)buf, sizeof(buf), NULL);
279 strcat(strcat(g->Message, ": "), buf);
280 } else if (!(CreateJavaVM = (CRTJVM)GetProcAddress((HINSTANCE)LibJvm,
281 "JNI_CreateJavaVM"))) {
282 sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(), "JNI_CreateJavaVM");
283 FreeLibrary((HMODULE)LibJvm);
284 LibJvm = NULL;
285 } else if (!(GetCreatedJavaVMs = (GETJVM)GetProcAddress((HINSTANCE)LibJvm,
286 "JNI_GetCreatedJavaVMs"))) {
287 sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(), "JNI_GetCreatedJavaVMs");
288 FreeLibrary((HMODULE)LibJvm);
289 LibJvm = NULL;
290 #if defined(_DEBUG)
291 } else if (!(GetDefaultJavaVMInitArgs = (GETDEF)GetProcAddress((HINSTANCE)LibJvm,
292 "JNI_GetDefaultJavaVMInitArgs"))) {
293 sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(),
294 "JNI_GetDefaultJavaVMInitArgs");
295 FreeLibrary((HMODULE)LibJvm);
296 LibJvm = NULL;
297 #endif // _DEBUG
298 } // endif LibJvm
299 #else // !_WIN32
300 const char *error = NULL;
301
302 for (ntry = 0; !LibJvm && ntry < 2; ntry++) {
303 if (!ntry && JvmPath) {
304 strcat(strcpy(soname, JvmPath), "/libjvm.so");
305 ntry = 2;
306 } else if (!ntry && getenv("JAVA_HOME")) {
307 // TODO: Replace i386 by a better guess
308 strcat(strcpy(soname, getenv("JAVA_HOME")), "/jre/lib/i386/client/libjvm.so");
309 } else { // Will need LD_LIBRARY_PATH to be set
310 strcpy(soname, "libjvm.so");
311 ntry = 2;
312 } // endelse
313
314 LibJvm = dlopen(soname, RTLD_LAZY);
315 } // endfor ntry
316
317 // Load the desired shared library
318 if (!LibJvm) {
319 error = dlerror();
320 sprintf(g->Message, MSG(SHARED_LIB_ERR), soname, SVP(error));
321 } else if (!(CreateJavaVM = (CRTJVM)dlsym(LibJvm, "JNI_CreateJavaVM"))) {
322 error = dlerror();
323 sprintf(g->Message, MSG(GET_FUNC_ERR), "JNI_CreateJavaVM", SVP(error));
324 dlclose(LibJvm);
325 LibJvm = NULL;
326 } else if (!(GetCreatedJavaVMs = (GETJVM)dlsym(LibJvm, "JNI_GetCreatedJavaVMs"))) {
327 error = dlerror();
328 sprintf(g->Message, MSG(GET_FUNC_ERR), "JNI_GetCreatedJavaVMs", SVP(error));
329 dlclose(LibJvm);
330 LibJvm = NULL;
331 #if defined(_DEBUG)
332 } else if (!(GetDefaultJavaVMInitArgs = (GETDEF)dlsym(LibJvm,
333 "JNI_GetDefaultJavaVMInitArgs"))) {
334 error = dlerror();
335 sprintf(g->Message, MSG(GET_FUNC_ERR), "JNI_GetDefaultJavaVMInitArgs", SVP(error));
336 dlclose(LibJvm);
337 LibJvm = NULL;
338 #endif // _DEBUG
339 } // endif LibJvm
340 #endif // !_WIN32
341
342 } // endif LibJvm
343
344 return LibJvm == NULL;
345 } // end of GetJVM
346
347 /***********************************************************************/
348 /* Open: connect to a data source. */
349 /***********************************************************************/
Open(PGLOBAL g)350 bool JAVAConn::Open(PGLOBAL g)
351 {
352 bool brc = true, err = false;
353 jboolean jt = (trace(1));
354
355 // Link or check whether jvm library was linked
356 if (GetJVM(g))
357 return true;
358
359 // Firstly check whether the jvm was already created
360 JavaVM* jvms[1];
361 jsize jsz;
362 jint rc = GetCreatedJavaVMs(jvms, 1, &jsz);
363
364 if (rc == JNI_OK && jsz == 1) {
365 // jvm already existing
366 jvm = jvms[0];
367 rc = jvm->AttachCurrentThread((void**)&env, nullptr);
368
369 if (rc != JNI_OK) {
370 strcpy(g->Message, "Cannot attach jvm to the current thread");
371 return true;
372 } // endif rc
373
374 } else {
375 /*******************************************************************/
376 /* Create a new jvm */
377 /*******************************************************************/
378 PSTRG jpop = new(g)STRING(g, 512, "-Djava.class.path=.");
379 char *cp = NULL;
380 char sep;
381
382 #if defined(_WIN32)
383 sep = ';';
384 #define N 1
385 //#define N 2
386 //#define N 3
387 #else
388 sep = ':';
389 #define N 1
390 #endif
391
392 // Add wrappers jar files
393 AddJars(jpop, sep);
394
395 //================== prepare loading of Java VM ============================
396 JavaVMInitArgs vm_args; // Initialization arguments
397 JavaVMOption* options = new JavaVMOption[N]; // JVM invocation options
398
399 // where to find java .class
400 if (ClassPath && *ClassPath) {
401 jpop->Append(sep);
402 jpop->Append(ClassPath);
403 } // endif ClassPath
404
405 // All wrappers are pre-compiled in JavaWrappers.jar in the share dir
406 jpop->Append(sep);
407 jpop->Append(GetMessageDir());
408 jpop->Append("JavaWrappers.jar");
409
410 #if defined(MONGO_SUPPORT)
411 jpop->Append(sep);
412 jpop->Append(GetMessageDir());
413 jpop->Append("Mongo3.jar");
414 jpop->Append(sep);
415 jpop->Append(GetMessageDir());
416 jpop->Append("Mongo2.jar");
417 #endif // MONGO_SUPPORT
418
419 if ((cp = getenv("CLASSPATH"))) {
420 jpop->Append(sep);
421 jpop->Append(cp);
422 } // endif cp
423
424 if (trace(1)) {
425 htrc("ClassPath=%s\n", ClassPath ? ClassPath : "null");
426 htrc("CLASSPATH=%s\n", cp ? cp : "null");
427 htrc("%s\n", jpop->GetStr());
428 } // endif trace
429
430 options[0].optionString = jpop->GetStr();
431 #if N == 2
432 options[1].optionString = "-Xcheck:jni";
433 #endif
434 #if N == 3
435 options[1].optionString = "-Xms256M";
436 options[2].optionString = "-Xmx512M";
437 #endif
438 #if defined(_DEBUG)
439 vm_args.version = JNI_VERSION_1_2; // minimum Java version
440 rc = GetDefaultJavaVMInitArgs(&vm_args);
441 #else
442 vm_args.version = JNI_VERSION_1_6; // minimum Java version
443 #endif // _DEBUG
444 vm_args.nOptions = N; // number of options
445 vm_args.options = options;
446 vm_args.ignoreUnrecognized = false; // invalid options make the JVM init fail
447
448 //=============== load and initialize Java VM and JNI interface =============
449 rc = CreateJavaVM(&jvm, (void**)&env, &vm_args); // YES !!
450 delete[] options; // we then no longer need the initialisation options.
451
452 switch (rc) {
453 case JNI_OK:
454 strcpy(g->Message, "VM successfully created");
455 brc = false;
456 break;
457 case JNI_ERR:
458 strcpy(g->Message, "Initialising JVM failed: unknown error");
459 break;
460 case JNI_EDETACHED:
461 strcpy(g->Message, "Thread detached from the VM");
462 break;
463 case JNI_EVERSION:
464 strcpy(g->Message, "JNI version error");
465 break;
466 case JNI_ENOMEM:
467 strcpy(g->Message, "Not enough memory");
468 break;
469 case JNI_EEXIST:
470 strcpy(g->Message, "VM already created");
471 break;
472 case JNI_EINVAL:
473 strcpy(g->Message, "Invalid arguments");
474 break;
475 default:
476 sprintf(g->Message, "Unknown return code %d", (int)rc);
477 break;
478 } // endswitch rc
479
480 if (trace(1))
481 htrc("%s\n", g->Message);
482
483 if (brc)
484 return true;
485
486 //=============== Display JVM version ===============
487 jint ver = env->GetVersion();
488 printf("JVM Version %d.%d\n", ((ver >> 16) & 0x0f), (ver & 0x0f));
489 } // endif rc
490
491 // try to find the java wrapper class
492 jdi = env->FindClass(m_Wrap);
493
494 if (jdi == nullptr) {
495 sprintf(g->Message, "ERROR: class %s not found!", m_Wrap);
496 return true;
497 } // endif jdi
498
499 #if 0 // Suppressed because it does not make any usable change
500 if (b && jpath && *jpath) {
501 // Try to add that path the the jvm class path
502 jmethodID alp = env->GetStaticMethodID(jdi, "addLibraryPath",
503 "(Ljava/lang/String;)I");
504
505 if (alp == nullptr) {
506 env->ExceptionDescribe();
507 env->ExceptionClear();
508 } else {
509 char *msg;
510 jstring path = env->NewStringUTF(jpath);
511 rc = env->CallStaticIntMethod(jdi, alp, path);
512
513 if ((msg = Check(rc))) {
514 strcpy(g->Message, msg);
515 env->DeleteLocalRef(path);
516 return RC_FX;
517 } else switch (rc) {
518 case JNI_OK:
519 printf("jpath added\n");
520 break;
521 case JNI_EEXIST:
522 printf("jpath already exist\n");
523 break;
524 case JNI_ERR:
525 default:
526 strcpy(g->Message, "Error adding jpath");
527 env->DeleteLocalRef(path);
528 return RC_FX;
529 } // endswitch rc
530
531 env->DeleteLocalRef(path);
532 } // endif alp
533
534 } // endif jpath
535 #endif // 0
536
537 // if class found, continue
538 jmethodID ctor = env->GetMethodID(jdi, "<init>", "(Z)V");
539
540 if (ctor == nullptr) {
541 sprintf(g->Message, "ERROR: %s constructor not found!", m_Wrap);
542 return true;
543 } else
544 job = env->NewObject(jdi, ctor, jt);
545
546 if (job == nullptr) {
547 sprintf(g->Message, "%s class object not constructed!", m_Wrap);
548 return true;
549 } // endif job
550
551 // If the object is successfully constructed,
552 // we can then search for the method we want to call,
553 // and invoke it for the object:
554 errid = env->GetMethodID(jdi, "GetErrmsg", "()Ljava/lang/String;");
555
556 if (env->ExceptionCheck()) {
557 strcpy(g->Message, "ERROR: method GetErrmsg() not found!");
558 env->ExceptionDescribe();
559 env->ExceptionClear();
560 return true;
561 } // endif Check
562
563 /*********************************************************************/
564 /* Link a Fblock. This make possible to automatically close it */
565 /* in case of error (throw). */
566 /*********************************************************************/
567 PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
568
569 fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
570 fp->Type = TYPE_FB_JAVA;
571 fp->Fname = NULL;
572 fp->Next = dbuserp->Openlist;
573 dbuserp->Openlist = fp;
574 fp->Count = 1;
575 fp->Length = 0;
576 fp->Memory = NULL;
577 fp->Mode = MODE_ANY;
578 fp->File = this;
579 fp->Handle = 0;
580
581 m_Opened = true;
582 return false;
583 } // end of Open
584
585 /***********************************************************************/
586 /* Disconnect connection */
587 /***********************************************************************/
Close()588 void JAVAConn::Close()
589 {
590 jint rc;
591
592 if (m_Connected) {
593 jmethodID did = nullptr;
594
595 // Could have been detached in case of join
596 rc = jvm->AttachCurrentThread((void**)&env, nullptr);
597
598 if (gmID(m_G, did, DiscFunc, "()I"))
599 printf("%s\n", Msg);
600 else if (Check(env->CallIntMethod(job, did)))
601 printf("%s: %s\n", DiscFunc, Msg);
602
603 m_Connected = false;
604 } // endif m_Connected
605
606 if ((rc = jvm->DetachCurrentThread()) != JNI_OK)
607 printf("DetachCurrentThread: rc=%d\n", (int)rc);
608
609 if (fp)
610 fp->Count = 0;
611
612 m_Opened = false;
613 } // end of Close
614