javaembed.cpp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include <jni.h>
  15. #include "jexcept.hpp"
  16. #include "jthread.hpp"
  17. #include "hqlplugins.hpp"
  18. #include "deftype.hpp"
  19. #include "eclrtl.hpp"
  20. #include "eclrtl_imp.hpp"
  21. #include "jprop.hpp"
  22. #include "build-config.h"
  23. #ifdef _WIN32
  24. #define EXPORT __declspec(dllexport)
  25. #else
  26. #define EXPORT
  27. #endif
  28. static const char * compatibleVersions[] = {
  29. "Java Embed Helper 1.0.0",
  30. NULL };
  31. static const char *version = "Java Embed Helper 1.0.0";
  32. extern "C" EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  33. {
  34. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  35. {
  36. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  37. pbx->compatibleVersions = compatibleVersions;
  38. }
  39. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  40. return false;
  41. pb->magicVersion = PLUGIN_VERSION;
  42. pb->version = version;
  43. pb->moduleName = "java";
  44. pb->ECL = NULL;
  45. pb->flags = PLUGIN_MULTIPLE_VERSIONS;
  46. pb->description = "Java Embed Helper";
  47. return true;
  48. }
  49. namespace javaembed {
  50. // Use a global object to ensure that the Java VM is initialized once only.
  51. // We would like to create it lazily for two reasons:
  52. // 1. So that we only get a JVM if we need one (even if we have loaded the plugin)
  53. // 2. It's important for the JVM to be initialized AFTER we have set up signal handlers, as it
  54. // likes to set its own (in particular, it seems to intercept and ignore some SIGSEGV during the
  55. // garbage collection).
  56. // Unfortunately, it seems that the design of the JNI interface is such that JNI_CreateJavaVM has to be called on the 'main thread'.
  57. // So we can't achieve 1, and 2 requires that we create via the INIT_MODULE mechanism (rather than just a static object), and that
  58. // any engines that call InitModuleObjects() or load plugins dynamically do so AFTER setting any signal handlers or calling
  59. // EnableSEHtoExceptionMapping
  60. //
  61. static class JavaGlobalState
  62. {
  63. public:
  64. JavaGlobalState()
  65. {
  66. JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
  67. StringArray optionStrings;
  68. const char* origPath = getenv("CLASSPATH");
  69. StringBuffer newPath;
  70. newPath.append("-Djava.class.path=");
  71. if (origPath && *origPath)
  72. {
  73. newPath.append(origPath).append(ENVSEPCHAR);
  74. }
  75. StringBuffer envConf;
  76. envConf.append(CONFIG_DIR).append(PATHSEPSTR).append("environment.conf");
  77. Owned<IProperties> conf = createProperties(envConf.str(), true);
  78. if (conf && conf->hasProp("classpath"))
  79. {
  80. conf->getProp("classpath", newPath);
  81. newPath.append(ENVSEPCHAR);
  82. }
  83. else
  84. {
  85. newPath.append(INSTALL_DIR).append(PATHSEPCHAR).append("classes").append(ENVSEPCHAR);
  86. }
  87. newPath.append(".");
  88. optionStrings.append(newPath);
  89. if (conf && conf->hasProp("jvmlibpath"))
  90. {
  91. StringBuffer libPath;
  92. libPath.append("-Djava.library.path=");
  93. conf->getProp("jvmlibpath", libPath);
  94. optionStrings.append(libPath);
  95. }
  96. if (conf && conf->hasProp("jvmoptions"))
  97. {
  98. optionStrings.appendList(conf->queryProp("jvmoptions"), ENVSEPSTR);
  99. }
  100. // Options we know we always want set
  101. optionStrings.append("-Xrs");
  102. // These may be useful for debugging
  103. // optionStrings.append("-Xcheck:jni");
  104. // optionStrings.append("-verbose:jni");
  105. JavaVMOption* options = new JavaVMOption[optionStrings.length()];
  106. ForEachItemIn(idx, optionStrings)
  107. {
  108. // DBGLOG("javaembed: Setting JVM option: %s",(char *)optionStrings.item(idx));
  109. options[idx].optionString = (char *) optionStrings.item(idx);
  110. options[idx].extraInfo = NULL;
  111. }
  112. vm_args.nOptions = optionStrings.length();
  113. vm_args.options = options;
  114. vm_args.ignoreUnrecognized = true;
  115. vm_args.version = JNI_VERSION_1_6;
  116. /* load and initialize a Java VM, return a JNI interface pointer in env */
  117. JNIEnv *env; /* receives pointer to native method interface */
  118. int createResult = JNI_CreateJavaVM(&javaVM, (void**)&env, &vm_args);
  119. delete [] options;
  120. if (createResult != 0)
  121. throw MakeStringException(0, "javaembed: Unable to initialize JVM (%d)",createResult);
  122. }
  123. ~JavaGlobalState()
  124. {
  125. // We don't attempt to destroy the Java VM, as it's buggy...
  126. }
  127. JavaVM *javaVM; /* denotes a Java VM */
  128. } *globalState;
  129. #ifdef _WIN32
  130. EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  131. #endif
  132. MODULE_INIT(INIT_PRIORITY_STANDARD)
  133. {
  134. globalState = new JavaGlobalState;
  135. // Make sure we are never unloaded (as JVM does not support it)
  136. // we do this by doing a dynamic load of the javaembed library
  137. #ifdef _WIN32
  138. char path[_MAX_PATH];
  139. ::GetModuleFileName((HINSTANCE)&__ImageBase, path, _MAX_PATH);
  140. if (strstr(path, "javaembed"))
  141. {
  142. HINSTANCE h = LoadSharedObject(path, false, false);
  143. DBGLOG("LoadSharedObject returned %p", h);
  144. }
  145. #else
  146. FILE *diskfp = fopen("/proc/self/maps", "r");
  147. if (diskfp)
  148. {
  149. char ln[_MAX_PATH];
  150. while (fgets(ln, sizeof(ln), diskfp))
  151. {
  152. if (strstr(ln, "libjavaembed"))
  153. {
  154. const char *fullName = strchr(ln, '/');
  155. if (fullName)
  156. {
  157. char *tail = (char *) strstr(fullName, SharedObjectExtension);
  158. if (tail)
  159. {
  160. tail[strlen(SharedObjectExtension)] = 0;
  161. HINSTANCE h = LoadSharedObject(fullName, false, false);
  162. break;
  163. }
  164. }
  165. }
  166. }
  167. fclose(diskfp);
  168. }
  169. #endif
  170. return true;
  171. }
  172. MODULE_EXIT()
  173. {
  174. // We don't attempt to destroy the Java VM, as it's buggy...
  175. // delete globalState;
  176. // globalState = NULL;
  177. }
  178. static void checkType(type_t javatype, size32_t javasize, type_t ecltype, size32_t eclsize)
  179. {
  180. if (javatype != ecltype || javasize != eclsize)
  181. throw MakeStringException(0, "javaembed: Type mismatch"); // MORE - could provide some details!
  182. }
  183. // There is a singleton JavaThreadContext per thread. This allows us to
  184. // ensure that we can make repeated calls to a Java function efficiently.
  185. class JavaThreadContext
  186. {
  187. public:
  188. JNIEnv *JNIenv; /* receives pointer to native method interface */
  189. public:
  190. JavaThreadContext()
  191. {
  192. jint res = globalState->javaVM->AttachCurrentThread((void **) &JNIenv, NULL);
  193. assertex(res >= 0);
  194. javaClass = NULL;
  195. javaMethodID = NULL;
  196. prevClassPath.set("dummy"); // Forces the call below to actually do something...
  197. setThreadClassLoader("");
  198. }
  199. ~JavaThreadContext()
  200. {
  201. if (javaClass)
  202. JNIenv->DeleteGlobalRef(javaClass);
  203. // According to the Java VM 1.7 docs, "A native thread attached to
  204. // the VM must call DetachCurrentThread() to detach itself before
  205. // exiting."
  206. globalState->javaVM->DetachCurrentThread();
  207. }
  208. void checkException()
  209. {
  210. if (JNIenv->ExceptionCheck())
  211. {
  212. jthrowable exception = JNIenv->ExceptionOccurred();
  213. JNIenv->ExceptionClear();
  214. jclass throwableClass = JNIenv->FindClass("java/lang/Throwable");
  215. jmethodID throwableToString = JNIenv->GetMethodID(throwableClass, "toString", "()Ljava/lang/String;");
  216. jstring cause = (jstring) JNIenv->CallObjectMethod(exception, throwableToString);
  217. const char *text = JNIenv->GetStringUTFChars(cause, 0);
  218. VStringBuffer message("javaembed: In method %s: %s", prevtext.get(), text);
  219. JNIenv->ReleaseStringUTFChars(cause, text);
  220. rtlFail(0, message.str());
  221. }
  222. }
  223. jobject getSystemClassLoader()
  224. {
  225. JNIenv->ExceptionClear();
  226. jclass javaLangClassLoaderClass = JNIenv->FindClass("java/lang/ClassLoader");
  227. checkException();
  228. jmethodID getSystemClassLoaderMethod = JNIenv->GetStaticMethodID(javaLangClassLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
  229. checkException();
  230. jobject systemClassLoaderObj = JNIenv->CallStaticObjectMethod(javaLangClassLoaderClass, getSystemClassLoaderMethod);
  231. checkException();
  232. assertex(systemClassLoaderObj);
  233. return systemClassLoaderObj;
  234. }
  235. void setThreadClassLoader(jobject classLoader)
  236. {
  237. JNIenv->ExceptionClear();
  238. jclass javaLangThreadClass = JNIenv->FindClass("java/lang/Thread");
  239. checkException();
  240. jmethodID currentThreadMethod = JNIenv->GetStaticMethodID(javaLangThreadClass, "currentThread", "()Ljava/lang/Thread;");
  241. checkException();
  242. jobject threadObj = JNIenv->CallStaticObjectMethod(javaLangThreadClass, currentThreadMethod);
  243. checkException();
  244. jmethodID setContextClassLoaderMethod = JNIenv->GetMethodID(javaLangThreadClass, "setContextClassLoader", "(Ljava/lang/ClassLoader;)V");
  245. checkException();
  246. JNIenv->CallObjectMethod(threadObj, setContextClassLoaderMethod, classLoader);
  247. checkException();
  248. }
  249. jobject getThreadClassLoader()
  250. {
  251. JNIenv->ExceptionClear();
  252. jclass javaLangThreadClass = JNIenv->FindClass("java/lang/Thread");
  253. checkException();
  254. jmethodID currentThreadMethod = JNIenv->GetStaticMethodID(javaLangThreadClass, "currentThread", "()Ljava/lang/Thread;");
  255. checkException();
  256. jobject threadObj = JNIenv->CallStaticObjectMethod(javaLangThreadClass, currentThreadMethod);
  257. checkException();
  258. jmethodID getContextClassLoaderMethod = JNIenv->GetMethodID(javaLangThreadClass, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
  259. checkException();
  260. jobject contextClassLoaderObj = JNIenv->CallObjectMethod(threadObj, getContextClassLoaderMethod);
  261. checkException();
  262. assertex(contextClassLoaderObj);
  263. return contextClassLoaderObj;
  264. }
  265. void setThreadClassLoader(const char *classPath)
  266. {
  267. if (classPath && *classPath)
  268. {
  269. if (prevClassPath && strcmp(classPath, prevClassPath) == 0)
  270. return;
  271. jclass URLcls = JNIenv->FindClass("java/net/URL");
  272. checkException();
  273. jmethodID URLclsMid = JNIenv->GetMethodID(URLcls, "<init>","(Ljava/lang/String;)V");
  274. checkException();
  275. StringArray paths;
  276. paths.appendList(classPath, ";"); // NOTE - as we need to be able to include : in the urls, we can't use ENVSEP here
  277. jobjectArray URLArray = JNIenv->NewObjectArray(paths.length(), URLcls, NULL);
  278. ForEachItemIn(idx, paths)
  279. {
  280. StringBuffer usepath;
  281. const char *path = paths.item(idx);
  282. if (!strchr(path, ':'))
  283. usepath.append("file:");
  284. usepath.append(path);
  285. jstring jstr = JNIenv->NewStringUTF(usepath.str());
  286. checkException();
  287. jobject URLobj = JNIenv->NewObject(URLcls, URLclsMid, jstr);
  288. checkException();
  289. JNIenv->SetObjectArrayElement(URLArray, idx, URLobj);
  290. JNIenv->DeleteLocalRef(URLobj);
  291. JNIenv->DeleteLocalRef(jstr);
  292. }
  293. checkException();
  294. jclass customLoaderClass = JNIenv->FindClass("java/net/URLClassLoader");
  295. checkException();
  296. jmethodID newInstance = JNIenv->GetStaticMethodID(customLoaderClass, "newInstance","([Ljava/net/URL;Ljava/lang/ClassLoader;)Ljava/net/URLClassLoader;");
  297. checkException();
  298. jobject contextClassLoaderObj = JNIenv->NewGlobalRef(JNIenv->CallStaticObjectMethod(customLoaderClass, newInstance, URLArray, getSystemClassLoader()));
  299. checkException();
  300. assertex(contextClassLoaderObj);
  301. setThreadClassLoader(contextClassLoaderObj);
  302. prevClassPath.set(classPath);
  303. }
  304. else
  305. {
  306. if (prevClassPath)
  307. setThreadClassLoader(getSystemClassLoader());
  308. prevClassPath.clear();
  309. }
  310. }
  311. inline void importFunction(size32_t lenChars, const char *utf, const char *options)
  312. {
  313. size32_t bytes = rtlUtf8Size(lenChars, utf);
  314. StringBuffer text(bytes, utf);
  315. setThreadClassLoader(options);
  316. if (!prevtext || strcmp(text, prevtext) != 0)
  317. {
  318. prevtext.clear();
  319. // Name should be in the form class.method:signature
  320. const char *funcname = strchr(text, '.');
  321. if (!funcname)
  322. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid import name %s - Expected classname.methodname:signature", text.str());
  323. const char *signature = strchr(funcname, ':');
  324. if (!signature)
  325. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid import name %s - Expected classname.methodname:signature", text.str());
  326. StringBuffer classname(funcname-text, text);
  327. funcname++; // skip the '.'
  328. StringBuffer methodname(signature-funcname, funcname);
  329. signature++; // skip the ':'
  330. if (javaClass)
  331. JNIenv->DeleteGlobalRef(javaClass);
  332. jobject classLoader = getThreadClassLoader();
  333. jmethodID loadClassMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(classLoader), "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
  334. jstring methodString = JNIenv->NewStringUTF(classname);
  335. javaClass = (jclass) JNIenv->NewGlobalRef(JNIenv->CallObjectMethod(classLoader, loadClassMethod, methodString));
  336. if (!javaClass)
  337. throw MakeStringException(MSGAUD_user, 0, "javaembed: Failed to resolve class name %s", classname.str());
  338. javaMethodID = JNIenv->GetStaticMethodID(javaClass, methodname, signature);
  339. if (!javaMethodID)
  340. throw MakeStringException(MSGAUD_user, 0, "javaembed: Failed to resolve method name %s with signature %s", methodname.str(), signature);
  341. const char *returnSig = strrchr(signature, ')');
  342. assertex(returnSig); // Otherwise how did Java accept it??
  343. returnSig++;
  344. returnType.set(returnSig);
  345. argsig.set(signature);
  346. prevtext.set(text);
  347. }
  348. }
  349. inline void callFunction(jvalue &result, const jvalue * args)
  350. {
  351. JNIenv->ExceptionClear();
  352. switch (returnType.get()[0])
  353. {
  354. case 'C': result.c = JNIenv->CallStaticCharMethodA(javaClass, javaMethodID, args); break;
  355. case 'Z': result.z = JNIenv->CallStaticBooleanMethodA(javaClass, javaMethodID, args); break;
  356. case 'J': result.j = JNIenv->CallStaticLongMethodA(javaClass, javaMethodID, args); break;
  357. case 'F': result.f = JNIenv->CallStaticFloatMethodA(javaClass, javaMethodID, args); break;
  358. case 'D': result.d = JNIenv->CallStaticDoubleMethodA(javaClass, javaMethodID, args); break;
  359. case 'I': result.i = JNIenv->CallStaticIntMethodA(javaClass, javaMethodID, args); break;
  360. case 'S': result.s = JNIenv->CallStaticShortMethodA(javaClass, javaMethodID, args); break;
  361. case 'B': result.s = JNIenv->CallStaticByteMethodA(javaClass, javaMethodID, args); break;
  362. case '[':
  363. case 'L': result.l = JNIenv->CallStaticObjectMethodA(javaClass, javaMethodID, args); break;
  364. default: throwUnexpected();
  365. }
  366. checkException();
  367. }
  368. inline __int64 getSignedResult(jvalue & result)
  369. {
  370. switch (returnType.get()[0])
  371. {
  372. case 'B': return result.b;
  373. case 'S': return result.s;
  374. case 'I': return result.i;
  375. case 'J': return result.j;
  376. case 'L':
  377. {
  378. // Result should be of class 'Number'
  379. if (!result.l)
  380. return 0;
  381. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "longValue", "()J");
  382. if (!getVal)
  383. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  384. return JNIenv->CallLongMethod(result.l, getVal);
  385. }
  386. default:
  387. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  388. }
  389. }
  390. inline double getDoubleResult(jvalue &result)
  391. {
  392. switch (returnType.get()[0])
  393. {
  394. case 'D': return result.d;
  395. case 'F': return result.f;
  396. case 'L':
  397. {
  398. // Result should be of class 'Number'
  399. if (!result.l)
  400. return 0;
  401. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "doubleValue", "()D");
  402. if (!getVal)
  403. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  404. return JNIenv->CallDoubleMethod(result.l, getVal);
  405. }
  406. default:
  407. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  408. }
  409. }
  410. inline bool getBooleanResult(jvalue &result)
  411. {
  412. switch (returnType.get()[0])
  413. {
  414. case 'Z': return result.z;
  415. case 'L':
  416. {
  417. // Result should be of class 'Boolean'
  418. if (!result.l)
  419. return false;
  420. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "booleanValue", "()Z");
  421. if (!getVal)
  422. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  423. return JNIenv->CallBooleanMethod(result.l, getVal);
  424. }
  425. default:
  426. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  427. }
  428. }
  429. inline void getDataResult(jvalue &result, size32_t &__len, void * &__result)
  430. {
  431. if (strcmp(returnType, "[B")!=0)
  432. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  433. jbyteArray array = (jbyteArray) result.l;
  434. __len = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  435. __result = (__len > 0 ? rtlMalloc(__len) : NULL);
  436. if (__result)
  437. JNIenv->GetByteArrayRegion(array, 0, __len, (jbyte *) __result);
  438. }
  439. inline void getStringResult(jvalue &result, size32_t &__len, char * &__result)
  440. {
  441. switch (returnType.get()[0])
  442. {
  443. case 'C': // Single char returned, prototyped as STRING or STRING1 in ECL
  444. rtlUnicodeToStrX(__len, __result, 1, &result.c);
  445. break;
  446. case 'L':
  447. {
  448. jstring sresult = (jstring) result.l;
  449. if (sresult)
  450. {
  451. size_t size = JNIenv->GetStringUTFLength(sresult); // in bytes
  452. const char *text = JNIenv->GetStringUTFChars(sresult, NULL);
  453. size32_t chars = rtlUtf8Length(size, text);
  454. rtlUtf8ToStrX(__len, __result, chars, text);
  455. JNIenv->ReleaseStringUTFChars(sresult, text);
  456. }
  457. else
  458. {
  459. __len = 0;
  460. __result = NULL;
  461. }
  462. break;
  463. }
  464. default:
  465. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  466. }
  467. }
  468. inline void getUTF8Result(jvalue &result, size32_t &__chars, char * &__result)
  469. {
  470. switch (returnType.get()[0])
  471. {
  472. case 'C': // Single jchar returned, prototyped as UTF8 in ECL
  473. rtlUnicodeToUtf8X(__chars, __result, 1, &result.c);
  474. break;
  475. case 'L':
  476. {
  477. jstring sresult = (jstring) result.l;
  478. if (sresult)
  479. {
  480. size_t size = JNIenv->GetStringUTFLength(sresult); // Returns length in bytes (not chars)
  481. const char * text = JNIenv->GetStringUTFChars(sresult, NULL);
  482. rtlUtf8ToUtf8X(__chars, __result, rtlUtf8Length(size, text), text);
  483. JNIenv->ReleaseStringUTFChars(sresult, text);
  484. }
  485. else
  486. {
  487. __chars = 0;
  488. __result = NULL;
  489. }
  490. break;
  491. }
  492. default:
  493. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  494. }
  495. }
  496. inline void getUnicodeResult(jvalue &result, size32_t &__chars, UChar * &__result)
  497. {
  498. switch (returnType.get()[0])
  499. {
  500. case 'C': // Single jchar returned, prototyped as UNICODE or UNICODE1 in ECL
  501. rtlUnicodeToUnicodeX(__chars, __result, 1, &result.c);
  502. break;
  503. case 'L':
  504. {
  505. jstring sresult = (jstring) result.l;
  506. if (sresult)
  507. {
  508. size_t size = JNIenv->GetStringUTFLength(sresult); // in bytes
  509. const char *text = JNIenv->GetStringUTFChars(sresult, NULL);
  510. size32_t chars = rtlUtf8Length(size, text);
  511. rtlUtf8ToUnicodeX(__chars, __result, chars, text);
  512. JNIenv->ReleaseStringUTFChars(sresult, text);
  513. }
  514. else
  515. {
  516. __chars = 0;
  517. __result = NULL;
  518. }
  519. break;
  520. }
  521. default:
  522. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  523. }
  524. }
  525. inline void getSetResult(jvalue &result, bool & __isAllResult, size32_t & __resultBytes, void * & __result, int _elemType, size32_t elemSize)
  526. {
  527. if (returnType.get()[0] != '[')
  528. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result (array expected)");
  529. type_t elemType = (type_t) _elemType;
  530. jarray array = (jarray) result.l;
  531. int numResults = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  532. rtlRowBuilder out;
  533. byte *outData = NULL;
  534. size32_t outBytes = 0;
  535. if (numResults > 0)
  536. {
  537. if (elemSize != UNKNOWN_LENGTH)
  538. {
  539. out.ensureAvailable(numResults * elemSize); // MORE - check for overflow?
  540. outData = out.getbytes();
  541. }
  542. switch(returnType.get()[1])
  543. {
  544. case 'Z':
  545. checkType(type_boolean, sizeof(jboolean), elemType, elemSize);
  546. JNIenv->GetBooleanArrayRegion((jbooleanArray) array, 0, numResults, (jboolean *) outData);
  547. break;
  548. case 'B':
  549. checkType(type_int, sizeof(jbyte), elemType, elemSize);
  550. JNIenv->GetByteArrayRegion((jbyteArray) array, 0, numResults, (jbyte *) outData);
  551. break;
  552. case 'C':
  553. // we COULD map to a set of string1, but is there any point?
  554. throw MakeStringException(0, "javaembed: Return type mismatch (char[] not supported)");
  555. break;
  556. case 'S':
  557. checkType(type_int, sizeof(jshort), elemType, elemSize);
  558. JNIenv->GetShortArrayRegion((jshortArray) array, 0, numResults, (jshort *) outData);
  559. break;
  560. case 'I':
  561. checkType(type_int, sizeof(jint), elemType, elemSize);
  562. JNIenv->GetIntArrayRegion((jintArray) array, 0, numResults, (jint *) outData);
  563. break;
  564. case 'J':
  565. checkType(type_int, sizeof(jlong), elemType, elemSize);
  566. JNIenv->GetLongArrayRegion((jlongArray) array, 0, numResults, (jlong *) outData);
  567. break;
  568. case 'F':
  569. checkType(type_real, sizeof(jfloat), elemType, elemSize);
  570. JNIenv->GetFloatArrayRegion((jfloatArray) array, 0, numResults, (jfloat *) outData);
  571. break;
  572. case 'D':
  573. checkType(type_real, sizeof(jdouble), elemType, elemSize);
  574. JNIenv->GetDoubleArrayRegion((jdoubleArray) array, 0, numResults, (jdouble *) outData);
  575. break;
  576. case 'L':
  577. if (strcmp(returnType, "[Ljava/lang/String;") == 0)
  578. {
  579. for (int i = 0; i < numResults; i++)
  580. {
  581. jstring elem = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) array, i);
  582. size_t lenBytes = JNIenv->GetStringUTFLength(elem); // in bytes
  583. const char *text = JNIenv->GetStringUTFChars(elem, NULL);
  584. switch (elemType)
  585. {
  586. case type_string:
  587. if (elemSize == UNKNOWN_LENGTH)
  588. {
  589. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  590. outData = out.getbytes() + outBytes;
  591. * (size32_t *) outData = lenBytes;
  592. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  593. outBytes += lenBytes + sizeof(size32_t);
  594. }
  595. else
  596. rtlStrToStr(elemSize, outData, lenBytes, text);
  597. break;
  598. case type_varstring:
  599. if (elemSize == UNKNOWN_LENGTH)
  600. {
  601. out.ensureAvailable(outBytes + lenBytes + 1);
  602. outData = out.getbytes() + outBytes;
  603. rtlStrToVStr(0, outData, lenBytes, text);
  604. outBytes += lenBytes + 1;
  605. }
  606. else
  607. rtlStrToVStr(elemSize, outData, lenBytes, text); // Fixed size null terminated strings... weird.
  608. break;
  609. case type_utf8:
  610. case type_unicode:
  611. {
  612. size32_t numchars = rtlUtf8Length(lenBytes, text);
  613. if (elemType == type_utf8)
  614. {
  615. assertex (elemSize == UNKNOWN_LENGTH);
  616. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  617. outData = out.getbytes() + outBytes;
  618. * (size32_t *) outData = numchars;
  619. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  620. outBytes += lenBytes + sizeof(size32_t);
  621. }
  622. else
  623. {
  624. if (elemSize == UNKNOWN_LENGTH)
  625. {
  626. // You can't assume that number of chars in utf8 matches number in unicode16 ...
  627. size32_t numchars16;
  628. rtlDataAttr unicode16;
  629. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  630. out.ensureAvailable(outBytes + numchars16*sizeof(UChar) + sizeof(size32_t));
  631. outData = out.getbytes() + outBytes;
  632. * (size32_t *) outData = numchars16;
  633. rtlUnicodeToUnicode(numchars16, (UChar *) (outData+sizeof(size32_t)), numchars16, unicode16.getustr());
  634. outBytes += numchars16*sizeof(UChar) + sizeof(size32_t);
  635. }
  636. else
  637. rtlUtf8ToUnicode(elemSize / sizeof(UChar), (UChar *) outData, numchars, text);
  638. }
  639. break;
  640. }
  641. default:
  642. JNIenv->ReleaseStringUTFChars(elem, text);
  643. throw MakeStringException(0, "javaembed: Return type mismatch (ECL string type expected)");
  644. }
  645. JNIenv->ReleaseStringUTFChars(elem, text);
  646. JNIenv->DeleteLocalRef(elem);
  647. if (elemSize != UNKNOWN_LENGTH)
  648. outData += elemSize;
  649. }
  650. }
  651. else
  652. throw MakeStringException(0, "javaembed: Return type mismatch (%s[] not supported)", returnType.get()+2);
  653. break;
  654. }
  655. }
  656. __isAllResult = false;
  657. __resultBytes = elemSize == UNKNOWN_LENGTH ? outBytes : elemSize * numResults;
  658. __result = out.detachdata();
  659. }
  660. inline const char *querySignature()
  661. {
  662. return argsig.get();
  663. }
  664. private:
  665. StringAttr returnType;
  666. StringAttr argsig;
  667. StringAttr prevtext;
  668. StringAttr prevClassPath;
  669. jclass javaClass;
  670. jmethodID javaMethodID;
  671. };
  672. // Each call to a Java function will use a new JavaEmbedScriptContext object
  673. #define MAX_JNI_ARGS 10
  674. class JavaEmbedImportContext : public CInterfaceOf<IEmbedFunctionContext>
  675. {
  676. public:
  677. JavaEmbedImportContext(JavaThreadContext *_sharedCtx, const char *options)
  678. : sharedCtx(_sharedCtx)
  679. {
  680. argcount = 0;
  681. argsig = NULL;
  682. StringArray opts;
  683. opts.appendList(options, ",");
  684. ForEachItemIn(idx, opts)
  685. {
  686. const char *opt = opts.item(idx);
  687. const char *val = strchr(opt, '=');
  688. if (val)
  689. {
  690. StringBuffer optName(val-opt, opt);
  691. val++;
  692. if (stricmp(optName, "classpath")==0)
  693. classpath.set(val);
  694. else
  695. throw MakeStringException(0, "javaembed: Unknown option %s", optName.str());
  696. }
  697. }
  698. // Create a new frame for local references and increase the capacity
  699. // of those references to 64 (default is 16)
  700. sharedCtx->JNIenv->PushLocalFrame(64);
  701. }
  702. ~JavaEmbedImportContext()
  703. {
  704. // Pop local reference frame; explicitly frees all local
  705. // references made during that frame's lifetime
  706. sharedCtx->JNIenv->PopLocalFrame(NULL);
  707. }
  708. virtual bool getBooleanResult()
  709. {
  710. return sharedCtx->getBooleanResult(result);
  711. }
  712. virtual void getDataResult(size32_t &__len, void * &__result)
  713. {
  714. sharedCtx->getDataResult(result, __len, __result);
  715. }
  716. virtual double getRealResult()
  717. {
  718. return sharedCtx->getDoubleResult(result);
  719. }
  720. virtual __int64 getSignedResult()
  721. {
  722. return sharedCtx->getSignedResult(result);
  723. }
  724. virtual unsigned __int64 getUnsignedResult()
  725. {
  726. throw MakeStringException(MSGAUD_user, 0, "javaembed: Unsigned results not supported"); // Java doesn't support unsigned
  727. }
  728. virtual void getStringResult(size32_t &__len, char * &__result)
  729. {
  730. sharedCtx->getStringResult(result, __len, __result);
  731. }
  732. virtual void getUTF8Result(size32_t &__chars, char * &__result)
  733. {
  734. sharedCtx->getUTF8Result(result, __chars, __result);
  735. }
  736. virtual void getUnicodeResult(size32_t &__chars, UChar * &__result)
  737. {
  738. sharedCtx->getUnicodeResult(result, __chars, __result);
  739. }
  740. virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
  741. {
  742. sharedCtx->getSetResult(result, __isAllResult, __resultBytes, __result, elemType, elemSize);
  743. }
  744. virtual IRowStream *getDatasetResult(IEngineRowAllocator * _resultAllocator)
  745. {
  746. UNIMPLEMENTED;
  747. }
  748. virtual byte * getRowResult(IEngineRowAllocator * _resultAllocator)
  749. {
  750. UNIMPLEMENTED;
  751. }
  752. virtual size32_t getTransformResult(ARowBuilder & builder)
  753. {
  754. UNIMPLEMENTED;
  755. }
  756. virtual void bindBooleanParam(const char *name, bool val)
  757. {
  758. if (*argsig != 'B')
  759. typeError("BOOLEAN");
  760. argsig++;
  761. jvalue v;
  762. v.z = val;
  763. addArg(v);
  764. }
  765. virtual void bindDataParam(const char *name, size32_t len, const void *val)
  766. {
  767. if (argsig[0] != '[' || argsig[1] != 'B')
  768. typeError("DATA");
  769. argsig += 2;
  770. jvalue v;
  771. jbyteArray javaData = sharedCtx->JNIenv->NewByteArray(len);
  772. sharedCtx->JNIenv->SetByteArrayRegion(javaData, 0, len, (jbyte *) val);
  773. v.l = javaData;
  774. addArg(v);
  775. }
  776. virtual void bindRealParam(const char *name, double val)
  777. {
  778. jvalue v;
  779. switch(*argsig)
  780. {
  781. case 'D':
  782. v.d = val;
  783. break;
  784. case 'F':
  785. v.f = val;
  786. break;
  787. default:
  788. typeError("REAL");
  789. break;
  790. }
  791. argsig++;
  792. addArg(v);
  793. }
  794. virtual void bindSignedParam(const char *name, __int64 val)
  795. {
  796. jvalue v;
  797. switch(*argsig)
  798. {
  799. case 'I':
  800. v.i = val;
  801. break;
  802. case 'J':
  803. v.j = val;
  804. break;
  805. case 'S':
  806. v.s = val;
  807. break;
  808. case 'B':
  809. v.b = val;
  810. break;
  811. default:
  812. typeError("INTEGER");
  813. break;
  814. }
  815. argsig++;
  816. addArg(v);
  817. }
  818. virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
  819. {
  820. throw MakeStringException(MSGAUD_user, 0, "javaembed: Unsigned parameters not supported"); // Java doesn't support unsigned
  821. }
  822. virtual void bindStringParam(const char *name, size32_t len, const char *val)
  823. {
  824. jvalue v;
  825. switch(*argsig)
  826. {
  827. case 'C':
  828. rtlStrToUnicode(1, &v.c, len, val);
  829. argsig++;
  830. break;
  831. case 'L':
  832. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  833. {
  834. argsig += 18;
  835. unsigned unicodeChars;
  836. UChar *unicode;
  837. rtlStrToUnicodeX(unicodeChars, unicode, len, val);
  838. v.l = sharedCtx->JNIenv->NewString(unicode, unicodeChars);
  839. rtlFree(unicode);
  840. break;
  841. }
  842. // fall into ...
  843. default:
  844. typeError("STRING");
  845. break;
  846. }
  847. addArg(v);
  848. }
  849. virtual void bindVStringParam(const char *name, const char *val)
  850. {
  851. bindStringParam(name, strlen(val), val);
  852. }
  853. virtual void bindUTF8Param(const char *name, size32_t numchars, const char *val)
  854. {
  855. jvalue v;
  856. switch(*argsig)
  857. {
  858. case 'C':
  859. rtlUtf8ToUnicode(1, &v.c, numchars, val);
  860. argsig++;
  861. break;
  862. case 'L':
  863. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  864. {
  865. argsig += 18;
  866. unsigned unicodeChars;
  867. UChar *unicode;
  868. rtlUtf8ToUnicodeX(unicodeChars, unicode, numchars, val);
  869. v.l = sharedCtx->JNIenv->NewString(unicode, unicodeChars);
  870. rtlFree(unicode);
  871. break;
  872. }
  873. // fall into ...
  874. default:
  875. typeError("UTF8");
  876. break;
  877. }
  878. addArg(v);
  879. }
  880. virtual void bindUnicodeParam(const char *name, size32_t numchars, const UChar *val)
  881. {
  882. jvalue v;
  883. switch(*argsig)
  884. {
  885. case 'C':
  886. rtlUnicodeToUnicode(1, &v.c, numchars, val);
  887. argsig++;
  888. break;
  889. case 'L':
  890. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  891. {
  892. argsig += 18;
  893. v.l = sharedCtx->JNIenv->NewString(val, numchars);
  894. break;
  895. }
  896. // fall into ...
  897. default:
  898. typeError("UNICODE");
  899. break;
  900. }
  901. addArg(v);
  902. }
  903. virtual void bindSetParam(const char *name, int _elemType, size32_t elemSize, bool isAll, size32_t totalBytes, void *setData)
  904. {
  905. jvalue v;
  906. if (*argsig != '[')
  907. typeError("SET");
  908. argsig++;
  909. type_t elemType = (type_t) _elemType;
  910. int numElems = totalBytes / elemSize;
  911. switch(*argsig)
  912. {
  913. case 'Z':
  914. checkType(type_boolean, sizeof(jboolean), elemType, elemSize);
  915. v.l = sharedCtx->JNIenv->NewBooleanArray(numElems);
  916. sharedCtx->JNIenv->SetBooleanArrayRegion((jbooleanArray) v.l, 0, numElems, (jboolean *) setData);
  917. break;
  918. case 'B':
  919. checkType(type_int, sizeof(jbyte), elemType, elemSize);
  920. v.l = sharedCtx->JNIenv->NewByteArray(numElems);
  921. sharedCtx->JNIenv->SetByteArrayRegion((jbyteArray) v.l, 0, numElems, (jbyte *) setData);
  922. break;
  923. case 'C':
  924. // we COULD map to a set of string1, but is there any point?
  925. typeError("");
  926. break;
  927. case 'S':
  928. checkType(type_int, sizeof(jshort), elemType, elemSize);
  929. v.l = sharedCtx->JNIenv->NewShortArray(numElems);
  930. sharedCtx->JNIenv->SetShortArrayRegion((jshortArray) v.l, 0, numElems, (jshort *) setData);
  931. break;
  932. case 'I':
  933. checkType(type_int, sizeof(jint), elemType, elemSize);
  934. v.l = sharedCtx->JNIenv->NewIntArray(numElems);
  935. sharedCtx->JNIenv->SetIntArrayRegion((jintArray) v.l, 0, numElems, (jint *) setData);
  936. break;
  937. case 'J':
  938. checkType(type_int, sizeof(jlong), elemType, elemSize);
  939. v.l = sharedCtx->JNIenv->NewLongArray(numElems);
  940. sharedCtx->JNIenv->SetLongArrayRegion((jlongArray) v.l, 0, numElems, (jlong *) setData);
  941. break;
  942. case 'F':
  943. checkType(type_real, sizeof(jfloat), elemType, elemSize);
  944. v.l = sharedCtx->JNIenv->NewFloatArray(numElems);
  945. sharedCtx->JNIenv->SetFloatArrayRegion((jfloatArray) v.l, 0, numElems, (jfloat *) setData);
  946. break;
  947. case 'D':
  948. checkType(type_real, sizeof(jdouble), elemType, elemSize);
  949. v.l = sharedCtx->JNIenv->NewDoubleArray(numElems);
  950. sharedCtx->JNIenv->SetDoubleArrayRegion((jdoubleArray) v.l, 0, numElems, (jdouble *) setData);
  951. break;
  952. case 'L':
  953. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  954. {
  955. argsig += 17; // Yes, 17, because we increment again at the end of the case
  956. const byte *inData = (const byte *) setData;
  957. const byte *endData = inData + totalBytes;
  958. if (elemSize == UNKNOWN_LENGTH)
  959. {
  960. numElems = 0;
  961. // Will need 2 passes to work out how many elements there are in the set :(
  962. while (inData < endData)
  963. {
  964. int thisSize;
  965. switch (elemType)
  966. {
  967. case type_varstring:
  968. thisSize = strlen((const char *) inData) + 1;
  969. break;
  970. case type_string:
  971. thisSize = * (size32_t *) inData + sizeof(size32_t);
  972. break;
  973. case type_unicode:
  974. thisSize = (* (size32_t *) inData) * sizeof(UChar) + sizeof(size32_t);
  975. break;
  976. case type_utf8:
  977. thisSize = rtlUtf8Size(* (size32_t *) inData, inData + sizeof(size32_t)) + sizeof(size32_t);;
  978. break;
  979. default:
  980. typeError("STRING");
  981. }
  982. inData += thisSize;
  983. numElems++;
  984. }
  985. inData = (const byte *) setData;
  986. }
  987. int idx = 0;
  988. v.l = sharedCtx->JNIenv->NewObjectArray(numElems, sharedCtx->JNIenv->FindClass("java/lang/String"), NULL);
  989. while (inData < endData)
  990. {
  991. jstring thisElem;
  992. size32_t thisSize = elemSize;
  993. switch (elemType)
  994. {
  995. case type_varstring:
  996. {
  997. size32_t numChars = strlen((const char *) inData);
  998. unsigned unicodeChars;
  999. rtlDataAttr unicode;
  1000. rtlStrToUnicodeX(unicodeChars, unicode.refustr(), numChars, (const char *) inData);
  1001. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  1002. if (elemSize == UNKNOWN_LENGTH)
  1003. thisSize = numChars + 1;
  1004. break;
  1005. }
  1006. case type_string:
  1007. {
  1008. if (elemSize == UNKNOWN_LENGTH)
  1009. {
  1010. thisSize = * (size32_t *) inData;
  1011. inData += sizeof(size32_t);
  1012. }
  1013. unsigned unicodeChars;
  1014. rtlDataAttr unicode;
  1015. rtlStrToUnicodeX(unicodeChars, unicode.refustr(), thisSize, (const char *) inData);
  1016. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  1017. break;
  1018. }
  1019. case type_unicode:
  1020. {
  1021. if (elemSize == UNKNOWN_LENGTH)
  1022. {
  1023. thisSize = (* (size32_t *) inData) * sizeof(UChar); // NOTE - it's in chars...
  1024. inData += sizeof(size32_t);
  1025. }
  1026. thisElem = sharedCtx->JNIenv->NewString((const UChar *) inData, thisSize / sizeof(UChar));
  1027. //checkJPythonError();
  1028. break;
  1029. }
  1030. case type_utf8:
  1031. {
  1032. assertex (elemSize == UNKNOWN_LENGTH);
  1033. size32_t numChars = * (size32_t *) inData;
  1034. inData += sizeof(size32_t);
  1035. unsigned unicodeChars;
  1036. rtlDataAttr unicode;
  1037. rtlUtf8ToUnicodeX(unicodeChars, unicode.refustr(), numChars, (const char *) inData);
  1038. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  1039. thisSize = rtlUtf8Size(numChars, inData);
  1040. break;
  1041. }
  1042. default:
  1043. typeError("STRING");
  1044. }
  1045. sharedCtx->checkException();
  1046. inData += thisSize;
  1047. sharedCtx->JNIenv->SetObjectArrayElement((jobjectArray) v.l, idx, thisElem);
  1048. sharedCtx->JNIenv->DeleteLocalRef(thisElem);
  1049. idx++;
  1050. }
  1051. }
  1052. else
  1053. typeError("");
  1054. break;
  1055. }
  1056. argsig++;
  1057. addArg(v);
  1058. }
  1059. virtual void bindRowParam(const char *name, IOutputMetaData & metaVal, byte *val)
  1060. {
  1061. UNIMPLEMENTED;
  1062. }
  1063. virtual void bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val)
  1064. {
  1065. UNIMPLEMENTED;
  1066. }
  1067. virtual void importFunction(size32_t lenChars, const char *utf)
  1068. {
  1069. sharedCtx->importFunction(lenChars, utf, classpath);
  1070. argsig = sharedCtx->querySignature();
  1071. assertex(*argsig == '(');
  1072. argsig++;
  1073. }
  1074. virtual void callFunction()
  1075. {
  1076. sharedCtx->callFunction(result, args);
  1077. }
  1078. virtual void compileEmbeddedScript(size32_t lenChars, const char *script)
  1079. {
  1080. throwUnexpected(); // The java language helper supports only imported functions, not embedding java code in ECL.
  1081. }
  1082. protected:
  1083. JavaThreadContext *sharedCtx;
  1084. jvalue result;
  1085. StringAttr classpath;
  1086. private:
  1087. void typeError(const char *ECLtype) __attribute__((noreturn))
  1088. {
  1089. const char *javaType;
  1090. int javaLen = 0;
  1091. switch (*argsig)
  1092. {
  1093. case 'Z': javaType = "boolean"; break;
  1094. case 'B': javaType = "byte"; break;
  1095. case 'C': javaType = "char"; break;
  1096. case 'S': javaType = "short"; break;
  1097. case 'I': javaType = "int"; break;
  1098. case 'J': javaType = "long"; break;
  1099. case 'F': javaType = "float"; break;
  1100. case 'D': javaType = "double"; break;
  1101. case '[': javaType = "array"; break;
  1102. case 'L':
  1103. {
  1104. javaType = argsig+1;
  1105. const char *semi = strchr(argsig, ';');
  1106. if (semi)
  1107. javaLen = semi - javaType;
  1108. break;
  1109. }
  1110. case ')':
  1111. throw MakeStringException(0, "javaembed: Too many ECL parameters passed for Java signature %s", sharedCtx->querySignature());
  1112. default:
  1113. throw MakeStringException(0, "javaembed: Unrecognized character %c in java signature %s", *argsig, sharedCtx->querySignature());
  1114. }
  1115. if (!javaLen)
  1116. javaLen = strlen(argsig);
  1117. throw MakeStringException(0, "javaembed: ECL type %s cannot be passed to Java type %.*s", ECLtype, javaLen, javaType);
  1118. }
  1119. void addArg(jvalue &arg)
  1120. {
  1121. assertex(argcount < MAX_JNI_ARGS);
  1122. args[argcount] = arg;
  1123. argcount++;
  1124. }
  1125. jvalue args[MAX_JNI_ARGS];
  1126. int argcount;
  1127. const char *argsig;
  1128. };
  1129. static __thread JavaThreadContext* threadContext; // We reuse per thread, for speed
  1130. static __thread ThreadTermFunc threadHookChain;
  1131. static void releaseContext()
  1132. {
  1133. if (threadContext)
  1134. {
  1135. delete threadContext;
  1136. threadContext = NULL;
  1137. }
  1138. if (threadHookChain)
  1139. {
  1140. (*threadHookChain)();
  1141. threadHookChain = NULL;
  1142. }
  1143. }
  1144. class JavaEmbedContext : public CInterfaceOf<IEmbedContext>
  1145. {
  1146. public:
  1147. virtual IEmbedFunctionContext *createFunctionContext(bool isImport, const char *options)
  1148. {
  1149. if (!threadContext)
  1150. {
  1151. threadContext = new JavaThreadContext;
  1152. threadHookChain = addThreadTermFunc(releaseContext);
  1153. }
  1154. assertex(isImport);
  1155. return new JavaEmbedImportContext(threadContext, options);
  1156. }
  1157. };
  1158. extern IEmbedContext* getEmbedContext()
  1159. {
  1160. return new JavaEmbedContext;
  1161. }
  1162. } // namespace