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