javaembed.cpp 86 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323
  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 "eclhelper.hpp"
  20. #include "eclrtl.hpp"
  21. #include "eclrtl_imp.hpp"
  22. #include "rtlfield_imp.hpp"
  23. #include "rtlds_imp.hpp"
  24. #include "jprop.hpp"
  25. #include "build-config.h"
  26. #include "roxiemem.hpp"
  27. #include "nbcd.hpp"
  28. #ifdef _WIN32
  29. #define EXPORT __declspec(dllexport)
  30. #else
  31. #define EXPORT
  32. #endif
  33. static const char * compatibleVersions[] = {
  34. "Java Embed Helper 1.0.0",
  35. NULL };
  36. static const char *version = "Java Embed Helper 1.0.0";
  37. extern "C" EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  38. {
  39. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  40. {
  41. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  42. pbx->compatibleVersions = compatibleVersions;
  43. }
  44. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  45. return false;
  46. pb->magicVersion = PLUGIN_VERSION;
  47. pb->version = version;
  48. pb->moduleName = "java";
  49. pb->ECL = NULL;
  50. pb->flags = PLUGIN_MULTIPLE_VERSIONS;
  51. pb->description = "Java Embed Helper";
  52. return true;
  53. }
  54. static void UNSUPPORTED(const char *feature) __attribute__((noreturn));
  55. static void UNSUPPORTED(const char *feature)
  56. {
  57. throw MakeStringException(-1, "UNSUPPORTED feature: %s not supported in java plugin", feature);
  58. }
  59. namespace javaembed {
  60. // Use a global object to ensure that the Java VM is initialized once only.
  61. // We would like to create it lazily for two reasons:
  62. // 1. So that we only get a JVM if we need one (even if we have loaded the plugin)
  63. // 2. It's important for the JVM to be initialized AFTER we have set up signal handlers, as it
  64. // likes to set its own (in particular, it seems to intercept and ignore some SIGSEGV during the
  65. // garbage collection).
  66. // Unfortunately, it seems that the design of the JNI interface is such that JNI_CreateJavaVM has to be called on the 'main thread'.
  67. // 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
  68. // any engines that call InitModuleObjects() or load plugins dynamically do so AFTER setting any signal handlers or calling
  69. // EnableSEHtoExceptionMapping
  70. //
  71. static class JavaGlobalState
  72. {
  73. public:
  74. JavaGlobalState()
  75. {
  76. JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
  77. StringArray optionStrings;
  78. const char* origPath = getenv("CLASSPATH");
  79. StringBuffer newPath;
  80. newPath.append("-Djava.class.path=");
  81. if (origPath && *origPath)
  82. {
  83. newPath.append(origPath).append(ENVSEPCHAR);
  84. }
  85. StringBuffer envConf;
  86. envConf.append(CONFIG_DIR).append(PATHSEPSTR).append("environment.conf");
  87. Owned<IProperties> conf = createProperties(envConf.str(), true);
  88. if (conf && conf->hasProp("classpath"))
  89. {
  90. conf->getProp("classpath", newPath);
  91. newPath.append(ENVSEPCHAR);
  92. }
  93. else
  94. {
  95. newPath.append(INSTALL_DIR).append(PATHSEPCHAR).append("classes").append(ENVSEPCHAR);
  96. }
  97. newPath.append(".");
  98. optionStrings.append(newPath);
  99. if (conf && conf->hasProp("jvmlibpath"))
  100. {
  101. StringBuffer libPath;
  102. libPath.append("-Djava.library.path=");
  103. conf->getProp("jvmlibpath", libPath);
  104. optionStrings.append(libPath);
  105. }
  106. if (conf && conf->hasProp("jvmoptions"))
  107. {
  108. optionStrings.appendList(conf->queryProp("jvmoptions"), ENVSEPSTR);
  109. }
  110. // Options we know we always want set
  111. optionStrings.append("-Xrs");
  112. // These may be useful for debugging
  113. #ifdef _DEBUG
  114. // optionStrings.append("-Xcheck:jni");
  115. // optionStrings.append("-verbose:jni");
  116. #endif
  117. JavaVMOption* options = new JavaVMOption[optionStrings.length()];
  118. ForEachItemIn(idx, optionStrings)
  119. {
  120. // DBGLOG("javaembed: Setting JVM option: %s",(char *)optionStrings.item(idx));
  121. options[idx].optionString = (char *) optionStrings.item(idx);
  122. options[idx].extraInfo = NULL;
  123. }
  124. vm_args.nOptions = optionStrings.length();
  125. vm_args.options = options;
  126. vm_args.ignoreUnrecognized = true;
  127. vm_args.version = JNI_VERSION_1_6;
  128. /* load and initialize a Java VM, return a JNI interface pointer in env */
  129. JNIEnv *env; /* receives pointer to native method interface */
  130. int createResult = JNI_CreateJavaVM(&javaVM, (void**)&env, &vm_args);
  131. delete [] options;
  132. if (createResult != 0)
  133. throw MakeStringException(0, "javaembed: Unable to initialize JVM (%d)",createResult);
  134. }
  135. ~JavaGlobalState()
  136. {
  137. // We don't attempt to destroy the Java VM, as it's buggy...
  138. }
  139. JavaVM *javaVM; /* denotes a Java VM */
  140. } *globalState;
  141. static char helperLibraryName[_MAX_PATH];
  142. #ifdef _WIN32
  143. EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  144. #endif
  145. MODULE_INIT(INIT_PRIORITY_STANDARD)
  146. {
  147. globalState = new JavaGlobalState;
  148. // Make sure we are never unloaded (as JVM does not support it)
  149. // we do this by doing a dynamic load of the javaembed library
  150. #ifdef _WIN32
  151. ::GetModuleFileName((HINSTANCE)&__ImageBase, helperLibraryName, _MAX_PATH);
  152. if (strstr(path, "javaembed"))
  153. {
  154. HINSTANCE h = LoadSharedObject(helperLibraryName, false, false);
  155. DBGLOG("LoadSharedObject returned %p", h);
  156. }
  157. #else
  158. FILE *diskfp = fopen("/proc/self/maps", "r");
  159. if (diskfp)
  160. {
  161. char ln[_MAX_PATH];
  162. while (fgets(ln, sizeof(ln), diskfp))
  163. {
  164. if (strstr(ln, "libjavaembed"))
  165. {
  166. const char *fullName = strchr(ln, '/');
  167. if (fullName)
  168. {
  169. char *tail = (char *) strstr(fullName, SharedObjectExtension);
  170. if (tail)
  171. {
  172. tail[strlen(SharedObjectExtension)] = 0;
  173. strcpy(helperLibraryName, fullName);
  174. HINSTANCE h = LoadSharedObject(fullName, false, false);
  175. break;
  176. }
  177. }
  178. }
  179. }
  180. fclose(diskfp);
  181. }
  182. #endif
  183. return true;
  184. }
  185. MODULE_EXIT()
  186. {
  187. // We don't attempt to destroy the Java VM, as it's buggy...
  188. // delete globalState;
  189. // globalState = NULL;
  190. }
  191. static void checkType(type_t javatype, size32_t javasize, type_t ecltype, size32_t eclsize)
  192. {
  193. if (javatype != ecltype || javasize != eclsize)
  194. throw MakeStringException(0, "javaembed: Type mismatch"); // MORE - could provide some details!
  195. }
  196. static void checkException(JNIEnv *JNIenv)
  197. {
  198. if (JNIenv->ExceptionCheck())
  199. {
  200. jthrowable exception = JNIenv->ExceptionOccurred();
  201. JNIenv->ExceptionClear();
  202. jclass throwableClass = JNIenv->FindClass("java/lang/Throwable");
  203. jmethodID throwableToString = JNIenv->GetMethodID(throwableClass, "toString", "()Ljava/lang/String;");
  204. jstring cause = (jstring) JNIenv->CallObjectMethod(exception, throwableToString);
  205. const char *text = JNIenv->GetStringUTFChars(cause, 0);
  206. VStringBuffer message("javaembed: %s", text);
  207. JNIenv->ReleaseStringUTFChars(cause, text);
  208. rtlFail(0, message.str());
  209. }
  210. }
  211. //-------------------------------------------
  212. // A JavaObject accessor has common functionality shared by both the builders below (Java-> ECL and ECL->Java)
  213. class JavaObjectAccessor : public CInterface
  214. {
  215. protected:
  216. JavaObjectAccessor(JNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jobject _row)
  217. : JNIenv(_JNIenv), row(_row), outerRow(_outerRow), idx(0), limit(0), inSet(false), inDataSet(false)
  218. {
  219. Class = (jclass) JNIenv->NewGlobalRef(JNIenv->GetObjectClass(row));
  220. }
  221. JavaObjectAccessor(JNIEnv *_JNIenv, const RtlFieldInfo *_outerRow)
  222. : JNIenv(_JNIenv), outerRow(_outerRow), idx(0), limit(0), inSet(false), inDataSet(false)
  223. {
  224. row = NULL;
  225. Class = NULL;
  226. }
  227. ~JavaObjectAccessor()
  228. {
  229. // Unwind anything left on the stack (in case we had exceptions), to make sure the Class we release is the global one
  230. if (stack.length())
  231. Class = (jclass) stack.item(0);
  232. if (Class)
  233. JNIenv->DeleteGlobalRef(Class);
  234. }
  235. void push()
  236. {
  237. stack.append(Class);
  238. stack.append(row);
  239. }
  240. void pop()
  241. {
  242. row = (jobject) stack.pop();
  243. Class = (jclass) stack.pop();
  244. }
  245. jfieldID getFieldId(const RtlFieldInfo * field, const char *sig, const char *expected)
  246. {
  247. // MORE - if we are going to stream a dataset we really should be caching these somehow
  248. JNIenv->ExceptionClear();
  249. jfieldID fieldId = 0;
  250. if (sig)
  251. {
  252. if (inSet)
  253. {
  254. VStringBuffer arraySig("[%s", sig);
  255. fieldId = JNIenv->GetFieldID(Class, field->name->getAtomNamePtr(), arraySig.str());
  256. }
  257. else
  258. fieldId = JNIenv->GetFieldID(Class, field->name->getAtomNamePtr(), sig);
  259. }
  260. else
  261. {
  262. // Do it the hard way via reflection API
  263. // Equivalent java:
  264. // Field field = object.getClass().getDeclaredField(fieldName);
  265. jclass classClass =JNIenv->GetObjectClass(Class);
  266. checkException();
  267. jmethodID getDeclaredField = JNIenv->GetMethodID(classClass, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;" );
  268. checkException();
  269. jstring fieldName = JNIenv->NewStringUTF(field->name->getAtomNamePtr());
  270. checkException();
  271. jobject reflectedField = JNIenv->CallObjectMethod(Class, getDeclaredField, fieldName);
  272. checkException();
  273. fieldId = JNIenv->FromReflectedField(reflectedField);
  274. }
  275. if (!fieldId && expected)
  276. throw MakeStringException(0, "javaembed: Unable to retrieve field %s of type %s", field->name->getAtomNamePtr(), expected);
  277. if (expected)
  278. checkException();
  279. else
  280. JNIenv->ExceptionClear();
  281. return fieldId;
  282. }
  283. void checkException()
  284. {
  285. javaembed::checkException(JNIenv);
  286. }
  287. JNIEnv *JNIenv;
  288. jobject row;
  289. const RtlFieldInfo *outerRow;
  290. jclass Class;
  291. ConstPointerArray stack;
  292. unsigned idx;
  293. UnsignedArray idxStack;
  294. unsigned limit;
  295. bool inSet;
  296. bool inDataSet;
  297. };
  298. // A JavaRowBuilder object is used to construct an ECL row from a Java object
  299. class JavaRowBuilder : public JavaObjectAccessor, implements IFieldSource
  300. {
  301. public:
  302. IMPLEMENT_IINTERFACE;
  303. JavaRowBuilder(JNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jobject _row)
  304. : JavaObjectAccessor(_JNIenv, _outerRow, _row)
  305. {
  306. }
  307. virtual bool getBooleanResult(const RtlFieldInfo *field)
  308. {
  309. jboolean b;
  310. if (inSet)
  311. {
  312. JNIenv->GetBooleanArrayRegion((jbooleanArray) row, idx, 1, &b);
  313. }
  314. else
  315. {
  316. jfieldID fieldId = getFieldId(field, "Z", "boolean");
  317. b = JNIenv->GetBooleanField(row, fieldId);
  318. }
  319. checkException();
  320. return b;
  321. }
  322. virtual void getDataResult(const RtlFieldInfo *field, size32_t &__len, void * &__result)
  323. {
  324. jbyteArray array;
  325. if (inSet)
  326. {
  327. array = (jbyteArray) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  328. }
  329. else
  330. {
  331. jfieldID fieldId = getFieldId(field, "[B", "DATA");
  332. array = (jbyteArray) JNIenv->GetObjectField(row, fieldId);
  333. }
  334. checkException();
  335. __len = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  336. __result = (__len > 0 ? rtlMalloc(__len) : NULL);
  337. if (__result)
  338. JNIenv->GetByteArrayRegion(array, 0, __len, (jbyte *) __result);
  339. checkException();
  340. }
  341. virtual double getRealResult(const RtlFieldInfo *field)
  342. {
  343. double d;
  344. if (inSet)
  345. {
  346. float f;
  347. switch (field->size(NULL, NULL))
  348. {
  349. case 4:
  350. JNIenv->GetFloatArrayRegion((jfloatArray) row, idx, 1, &f);
  351. d = f;
  352. break;
  353. case 8:
  354. JNIenv->GetDoubleArrayRegion((jdoubleArray) row, idx, 1, &d);
  355. break;
  356. default:
  357. throwUnexpected();
  358. }
  359. }
  360. else
  361. {
  362. jfieldID fieldId;
  363. switch (field->size(NULL, NULL))
  364. {
  365. case 4:
  366. fieldId = getFieldId(field, "F", "float");
  367. d = JNIenv->GetFloatField(row, fieldId);
  368. break;
  369. case 8:
  370. fieldId = getFieldId(field, "D", "double");
  371. d = JNIenv->GetDoubleField(row, fieldId);
  372. break;
  373. default:
  374. throwUnexpected();
  375. }
  376. }
  377. checkException();
  378. return d;
  379. }
  380. virtual __int64 getSignedResult(const RtlFieldInfo *field)
  381. {
  382. __int64 ret;
  383. if (inSet)
  384. {
  385. jbyte b;
  386. jshort s;
  387. jint i;
  388. jlong l;
  389. switch (field->size(NULL, NULL))
  390. {
  391. case 1:
  392. JNIenv->GetByteArrayRegion((jbyteArray) row, idx, 1, &b);
  393. ret = b;
  394. break;
  395. case 2:
  396. JNIenv->GetShortArrayRegion((jshortArray) row, idx, 1, &s);
  397. ret = s;
  398. break;
  399. case 4:
  400. JNIenv->GetIntArrayRegion((jintArray) row, idx, 1, &i);
  401. ret = i;
  402. break;
  403. case 8:
  404. JNIenv->GetLongArrayRegion((jlongArray) row, idx, 1, &l);
  405. ret = l;
  406. break;
  407. default:
  408. UNSUPPORTED("non-standard integer sizes");
  409. }
  410. }
  411. else
  412. {
  413. jfieldID fieldId;
  414. switch (field->size(NULL, NULL))
  415. {
  416. case 1:
  417. fieldId = getFieldId(field, "B", "byte");
  418. ret = JNIenv->GetByteField(row, fieldId);
  419. break;
  420. case 2:
  421. fieldId = getFieldId(field, "S", "short");
  422. ret = JNIenv->GetShortField(row, fieldId);
  423. break;
  424. case 4:
  425. fieldId = getFieldId(field, "I", "int");
  426. ret = JNIenv->GetIntField(row, fieldId);
  427. break;
  428. case 8:
  429. fieldId = getFieldId(field, "J", "long");
  430. ret = JNIenv->GetLongField(row, fieldId);
  431. break;
  432. default:
  433. UNSUPPORTED("non-standard integer sizes");
  434. }
  435. }
  436. checkException();
  437. return ret;
  438. }
  439. virtual unsigned __int64 getUnsignedResult(const RtlFieldInfo *field)
  440. {
  441. UNSUPPORTED("unsigned fields"); // No unsigned types in Java
  442. }
  443. virtual void getStringResult(const RtlFieldInfo *field, size32_t &__len, char * &__result)
  444. {
  445. jstring result;
  446. if (inSet)
  447. {
  448. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  449. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  450. }
  451. else
  452. {
  453. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  454. {
  455. // See if there's a char field
  456. jfieldID charFieldId = getFieldId(field, "C", NULL);
  457. if (charFieldId)
  458. {
  459. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  460. rtlUnicodeToStrX(__len, __result, 1, &resultChar);
  461. return;
  462. }
  463. }
  464. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  465. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  466. }
  467. if (!result)
  468. {
  469. __len = 0;
  470. __result = NULL;
  471. return;
  472. }
  473. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  474. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  475. size32_t chars = rtlUtf8Length(size, text);
  476. rtlUtf8ToStrX(__len, __result, chars, text);
  477. JNIenv->ReleaseStringUTFChars(result, text);
  478. JNIenv->DeleteLocalRef(result);
  479. }
  480. virtual void getUTF8Result(const RtlFieldInfo *field, size32_t &__len, char * &__result)
  481. {
  482. jstring result;
  483. if (inSet)
  484. {
  485. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  486. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  487. }
  488. else
  489. {
  490. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  491. {
  492. // See if there's a char field
  493. jfieldID charFieldId = getFieldId(field, "C", NULL);
  494. if (charFieldId)
  495. {
  496. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  497. rtlUnicodeToUtf8X(__len, __result, 1, &resultChar);
  498. return;
  499. }
  500. }
  501. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  502. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  503. }
  504. if (!result)
  505. {
  506. __len = 0;
  507. __result = NULL;
  508. return;
  509. }
  510. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  511. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  512. size32_t chars = rtlUtf8Length(size, text);
  513. rtlUtf8ToUtf8X(__len, __result, chars, text);
  514. JNIenv->ReleaseStringUTFChars(result, text);
  515. JNIenv->DeleteLocalRef(result);
  516. }
  517. virtual void getUnicodeResult(const RtlFieldInfo *field, size32_t &__len, UChar * &__result)
  518. {
  519. jstring result;
  520. if (inSet)
  521. {
  522. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  523. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  524. }
  525. else
  526. {
  527. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  528. {
  529. // See if there's a char field
  530. jfieldID charFieldId = getFieldId(field, "C", NULL);
  531. if (charFieldId)
  532. {
  533. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  534. rtlUnicodeToUnicodeX(__len, __result, 1, &resultChar);
  535. return;
  536. }
  537. }
  538. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  539. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  540. }
  541. if (!result)
  542. {
  543. __len = 0;
  544. __result = NULL;
  545. return;
  546. }
  547. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  548. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  549. size32_t chars = rtlUtf8Length(size, text);
  550. rtlUtf8ToUnicodeX(__len, __result, chars, text);
  551. JNIenv->ReleaseStringUTFChars(result, text);
  552. JNIenv->DeleteLocalRef(result);
  553. }
  554. virtual void getDecimalResult(const RtlFieldInfo *field, Decimal &value)
  555. {
  556. double ret = getRealResult(field);
  557. value.setReal(ret);
  558. }
  559. virtual void processBeginSet(const RtlFieldInfo * field, bool &isAll)
  560. {
  561. isAll = false; // No concept of an 'all' set in Java
  562. push();
  563. jfieldID fieldId = getFieldId(field, NULL, "object"); // We assume it will be an array, but not sure of what...
  564. row = JNIenv->GetObjectField(row, fieldId);
  565. inSet = true;
  566. idx = -1; // First call to next() increments it to 0
  567. limit = row != NULL ? JNIenv->GetArrayLength((jarray) row) : 0;
  568. checkException();
  569. }
  570. virtual bool processNextSet(const RtlFieldInfo * field)
  571. {
  572. assertex(inSet);
  573. idx++;
  574. return idx < limit;
  575. }
  576. virtual void processBeginDataset(const RtlFieldInfo * field)
  577. {
  578. push();
  579. jfieldID fieldId = getFieldId(field, NULL, "object"); // We assume it will be an array, but not sure of what...
  580. row = JNIenv->GetObjectField(row, fieldId);
  581. inDataSet = true;
  582. idx = -1; // First call to next() increments it to 0
  583. limit = row != NULL ? JNIenv->GetArrayLength((jarray) row) : 0;
  584. checkException();
  585. }
  586. virtual void processBeginRow(const RtlFieldInfo * field)
  587. {
  588. if (field != outerRow)
  589. {
  590. push();
  591. if (inDataSet)
  592. {
  593. row = JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  594. }
  595. else
  596. {
  597. jfieldID fieldId = getFieldId(field, NULL, "object");
  598. row = JNIenv->GetObjectField(row, fieldId);
  599. }
  600. if (!row)
  601. rtlFail(0, "javaembed: child dataset object should not be NULL");
  602. Class = JNIenv->GetObjectClass(row);
  603. }
  604. }
  605. virtual bool processNextRow(const RtlFieldInfo * field)
  606. {
  607. assertex(inDataSet);
  608. idx++;
  609. return idx < limit;
  610. }
  611. virtual void processEndSet(const RtlFieldInfo * field)
  612. {
  613. inSet = false;
  614. JNIenv->DeleteLocalRef(row);
  615. pop();
  616. }
  617. virtual void processEndDataset(const RtlFieldInfo * field)
  618. {
  619. inDataSet = false;
  620. JNIenv->DeleteLocalRef(row);
  621. pop();
  622. }
  623. virtual void processEndRow(const RtlFieldInfo * field)
  624. {
  625. if (field != outerRow)
  626. {
  627. JNIenv->DeleteLocalRef(row);
  628. JNIenv->DeleteLocalRef(Class);
  629. pop();
  630. }
  631. }
  632. };
  633. //-------------------------------------------
  634. // A JavaObjectBuilder object is used to construct a Java object from an ECL row
  635. class JavaObjectBuilder : public JavaObjectAccessor, implements IFieldProcessor
  636. {
  637. public:
  638. IMPLEMENT_IINTERFACE;
  639. JavaObjectBuilder(JNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, const char *className)
  640. : JavaObjectAccessor(_JNIenv, _outerRow)
  641. {
  642. JNIenv->ExceptionClear();
  643. Class = (jclass) JNIenv->NewGlobalRef(JNIenv->FindClass(className)); // MORE - should use the custom classloader, once that fix is merged
  644. checkException();
  645. setConstructor();
  646. }
  647. virtual void processString(unsigned numchars, const char *text, const RtlFieldInfo * field)
  648. {
  649. if (field->isFixedSize() && field->size(NULL, NULL)==1 && !inSet) // SET OF STRING1 is not mapped to array of char...
  650. {
  651. // See if there's a char field
  652. jfieldID charFieldId = getFieldId(field, "C", NULL);
  653. if (charFieldId)
  654. {
  655. assertex(numchars==1);
  656. jchar c;
  657. rtlStrToUnicode(1, &c, 1, text);
  658. JNIenv->SetCharField(row, charFieldId, c);
  659. checkException();
  660. return;
  661. }
  662. }
  663. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  664. size32_t numchars16;
  665. rtlDataAttr unicode16;
  666. rtlStrToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  667. jstring value = JNIenv->NewString(unicode16.getustr(), numchars16);
  668. checkException();
  669. if (inSet)
  670. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  671. else
  672. JNIenv->SetObjectField(row, fieldId, value);
  673. JNIenv->DeleteLocalRef(value);
  674. checkException();
  675. }
  676. virtual void processBool(bool value, const RtlFieldInfo * field)
  677. {
  678. jfieldID fieldId = getFieldId(field, "Z", "boolean");
  679. JNIenv->SetBooleanField(row, fieldId, value);
  680. checkException();
  681. }
  682. virtual void processData(unsigned len, const void *value, const RtlFieldInfo * field)
  683. {
  684. jfieldID fieldId = getFieldId(field, "[B", "data");
  685. jbyteArray javaData = JNIenv->NewByteArray(len);
  686. JNIenv->SetByteArrayRegion(javaData, 0, len, (jbyte *) value);
  687. checkException();
  688. if (inSet)
  689. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, javaData);
  690. else
  691. JNIenv->SetObjectField(row, fieldId, javaData);
  692. checkException();
  693. }
  694. virtual void processInt(__int64 value, const RtlFieldInfo * field)
  695. {
  696. jfieldID fieldId;
  697. switch (field->size(NULL, NULL))
  698. {
  699. case 1:
  700. fieldId = getFieldId(field, "B", "byte");
  701. JNIenv->SetByteField(row, fieldId, value);
  702. break;
  703. case 2:
  704. fieldId = getFieldId(field, "S", "short");
  705. JNIenv->SetShortField(row, fieldId, value);
  706. break;
  707. case 4:
  708. fieldId = getFieldId(field, "I", "int");
  709. JNIenv->SetIntField(row, fieldId, value);
  710. break;
  711. case 8:
  712. fieldId = getFieldId(field, "J", "long");
  713. JNIenv->SetLongField(row, fieldId, value);
  714. break;
  715. default:
  716. UNSUPPORTED("non-standard integer sizes");
  717. break;
  718. }
  719. checkException();
  720. }
  721. virtual void processUInt(unsigned __int64 value, const RtlFieldInfo * field)
  722. {
  723. UNSUPPORTED("unsigned fields"); // No unsigned types in Java
  724. }
  725. virtual void processReal(double value, const RtlFieldInfo * field)
  726. {
  727. jfieldID fieldId;
  728. switch (field->size(NULL, NULL))
  729. {
  730. case 4:
  731. fieldId = getFieldId(field, "F", "float");
  732. JNIenv->SetFloatField(row, fieldId, (float) value);
  733. break;
  734. case 8:
  735. fieldId = getFieldId(field, "D", "double");
  736. JNIenv->SetDoubleField(row, fieldId, value);
  737. break;
  738. default:
  739. throwUnexpected();
  740. }
  741. checkException();
  742. }
  743. virtual void processDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  744. {
  745. // we could map to doubles, but probably better to let the ECL programmer do that themselves
  746. UNSUPPORTED("DECIMAL fields");
  747. }
  748. virtual void processUDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  749. {
  750. UNSUPPORTED("UDECIMAL fields");
  751. }
  752. virtual void processUnicode(unsigned numchars, const UChar *text, const RtlFieldInfo * field)
  753. {
  754. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  755. jstring value = JNIenv->NewString(text, numchars);
  756. checkException();
  757. if (inSet)
  758. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  759. else
  760. JNIenv->SetObjectField(row, fieldId, value);
  761. JNIenv->DeleteLocalRef(value);
  762. checkException();
  763. }
  764. virtual void processQString(unsigned len, const char *value, const RtlFieldInfo * field)
  765. {
  766. size32_t charCount;
  767. rtlDataAttr text;
  768. rtlQStrToStrX(charCount, text.refstr(), len, value);
  769. processString(charCount, text.getstr(), field);
  770. }
  771. virtual void processUtf8(unsigned numchars, const char *text, const RtlFieldInfo * field)
  772. {
  773. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  774. size32_t numchars16;
  775. rtlDataAttr unicode16;
  776. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  777. jstring value = JNIenv->NewString(unicode16.getustr(), numchars16);
  778. checkException();
  779. if (inSet)
  780. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  781. else
  782. JNIenv->SetObjectField(row, fieldId, value);
  783. JNIenv->DeleteLocalRef(value);
  784. checkException();
  785. }
  786. virtual bool processBeginSet(const RtlFieldInfo * field, unsigned numElems, bool isAll, const byte *data)
  787. {
  788. push();
  789. idx = 0;
  790. limit = numElems;
  791. const char *javaTypeSignature = NULL;
  792. bool processElements = false;
  793. // row needs to be created as an array of <whatever>
  794. if (isAll)
  795. UNSUPPORTED("ALL sets");
  796. const RtlTypeInfo *childType = field->type->queryChildType();
  797. jobject newRow;
  798. switch(childType->fieldType & RFTMkind)
  799. {
  800. case type_boolean:
  801. newRow = JNIenv->NewBooleanArray(numElems);
  802. JNIenv->SetBooleanArrayRegion((jbooleanArray) newRow, 0, numElems, (jboolean *) data);
  803. javaTypeSignature = "[Z";
  804. break;
  805. case type_int:
  806. if (childType->fieldType & RFTMunsigned)
  807. UNSUPPORTED("unsigned integers");
  808. switch (childType->length)
  809. {
  810. case 1:
  811. newRow = JNIenv->NewByteArray(numElems);
  812. JNIenv->SetByteArrayRegion((jbyteArray) newRow, 0, numElems, (jbyte *) data);
  813. javaTypeSignature = "[B";
  814. break;
  815. case 2:
  816. newRow = JNIenv->NewShortArray(numElems);
  817. JNIenv->SetShortArrayRegion((jshortArray) newRow, 0, numElems, (jshort *) data);
  818. javaTypeSignature = "[S";
  819. break;
  820. case 4:
  821. newRow = JNIenv->NewIntArray(numElems);
  822. JNIenv->SetIntArrayRegion((jintArray) newRow, 0, numElems, (jint *) data);
  823. javaTypeSignature = "[I";
  824. break;
  825. case 8:
  826. newRow = JNIenv->NewLongArray(numElems);
  827. JNIenv->SetLongArrayRegion((jlongArray) newRow, 0, numElems, (jlong *) data);
  828. javaTypeSignature = "[J";
  829. break;
  830. default:
  831. UNSUPPORTED("non-standard integer sizes");
  832. break;
  833. }
  834. break;
  835. case type_real:
  836. switch (childType->length)
  837. {
  838. case 4:
  839. newRow = JNIenv->NewFloatArray(numElems);
  840. JNIenv->SetFloatArrayRegion((jfloatArray) newRow, 0, numElems, (float *) data);
  841. javaTypeSignature = "[F";
  842. break;
  843. case 8:
  844. newRow = JNIenv->NewDoubleArray(numElems);
  845. JNIenv->SetDoubleArrayRegion((jdoubleArray) newRow, 0, numElems, (double *) data);
  846. javaTypeSignature = "[D";
  847. break;
  848. default:
  849. throwUnexpected();
  850. break;
  851. }
  852. break;
  853. case type_string:
  854. case type_varstring:
  855. case type_unicode:
  856. case type_utf8:
  857. newRow = JNIenv->NewObjectArray(numElems, JNIenv->FindClass("java/lang/String"), NULL);
  858. javaTypeSignature = "[Ljava/lang/String;";
  859. processElements = true;
  860. break;
  861. case type_data:
  862. newRow = JNIenv->NewObjectArray(numElems, JNIenv->FindClass("[B"), NULL);
  863. javaTypeSignature = "[[B";
  864. processElements = true;
  865. break;
  866. default:
  867. throwUnexpected();
  868. }
  869. checkException();
  870. jfieldID fieldId = getFieldId(field, javaTypeSignature, "Array");
  871. JNIenv->SetObjectField(row, fieldId, newRow);
  872. row = newRow;
  873. inSet = true;
  874. return processElements;
  875. }
  876. virtual bool processBeginDataset(const RtlFieldInfo * field, unsigned numRows)
  877. {
  878. push();
  879. idxStack.append(idx);
  880. idx = 0;
  881. inDataSet = true;
  882. // Create an empty array
  883. jfieldID childId = getFieldId( field, NULL, "RECORD");
  884. jobject newRow = NULL;
  885. if (numRows)
  886. {
  887. jclass arrayClass = getClassForChild(childId);
  888. jmethodID isArrayMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(arrayClass), "isArray", "()Z" );
  889. checkException();
  890. if (!JNIenv->CallBooleanMethod(arrayClass, isArrayMethod))
  891. {
  892. JNIenv->ExceptionClear();
  893. VStringBuffer message("javaembed: Array expected for field %s", field->name->getAtomNamePtr());
  894. rtlFail(0, message.str());
  895. }
  896. // Set up constructor etc for the child rows, so we don't do it per row
  897. jmethodID getTypeMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(arrayClass), "getComponentType", "()Ljava/lang/Class;" );
  898. checkException();
  899. Class = (jclass) JNIenv->CallObjectMethod(arrayClass, getTypeMethod);
  900. checkException();
  901. setConstructor();
  902. // Now we need to create the array
  903. newRow = JNIenv->NewObjectArray(numRows, Class, NULL);
  904. checkException();
  905. }
  906. JNIenv->SetObjectField(row, childId, newRow);
  907. checkException();
  908. row = newRow;
  909. return true;
  910. }
  911. virtual bool processBeginRow(const RtlFieldInfo * field)
  912. {
  913. if (field == outerRow)
  914. row = JNIenv->NewObject(Class, constructor);
  915. else
  916. {
  917. push();
  918. stack.append(constructor);
  919. // Now we have to create the child object
  920. jobject newRow = NULL;
  921. if (inDataSet)
  922. {
  923. newRow = JNIenv->NewObject(Class, constructor);
  924. checkException();
  925. JNIenv->SetObjectArrayElement((jobjectArray) row, idx++, newRow);
  926. }
  927. else
  928. {
  929. // All this is done once per dataset in the nested dataset case. But for embedded record case we have to do it here
  930. jfieldID childId = getFieldId( field, NULL, "RECORD");
  931. Class = getClassForChild(childId);
  932. setConstructor();
  933. newRow = JNIenv->NewObject(Class, constructor);
  934. checkException();
  935. JNIenv->SetObjectField(row, childId, newRow);
  936. }
  937. row = newRow;
  938. }
  939. checkException();
  940. return true;
  941. }
  942. virtual void processEndSet(const RtlFieldInfo * field)
  943. {
  944. JNIenv->DeleteLocalRef(row);
  945. pop();
  946. inSet = false;
  947. }
  948. virtual void processEndDataset(const RtlFieldInfo * field)
  949. {
  950. inDataSet = false;
  951. idx = idxStack.pop();
  952. pop();
  953. }
  954. virtual void processEndRow(const RtlFieldInfo * field)
  955. {
  956. if (field != outerRow)
  957. {
  958. constructor = (jmethodID) stack.pop();
  959. JNIenv->DeleteLocalRef(row);
  960. pop();
  961. }
  962. }
  963. inline jobject getObject()
  964. {
  965. return row;
  966. }
  967. protected:
  968. jclass getClassForChild(jfieldID childId)
  969. {
  970. jobject reflectedField = JNIenv->ToReflectedField(Class, childId, false);
  971. checkException();
  972. jclass fieldClass =JNIenv->GetObjectClass(reflectedField);
  973. checkException();
  974. jmethodID getTypeMethod = JNIenv->GetMethodID(fieldClass, "getType", "()Ljava/lang/Class;" );
  975. checkException();
  976. jclass result = (jclass) JNIenv->CallObjectMethod(reflectedField, getTypeMethod);
  977. checkException();
  978. JNIenv->DeleteLocalRef(reflectedField);
  979. JNIenv->DeleteLocalRef(fieldClass);
  980. return result;
  981. }
  982. void setConstructor()
  983. {
  984. constructor = JNIenv->GetMethodID(Class, "<init>", "()V");
  985. if (!constructor)
  986. {
  987. JNIenv->ExceptionClear();
  988. jmethodID getNameMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(Class), "getName", "()Ljava/lang/String;" );
  989. checkException();
  990. jstring name = (jstring) JNIenv->CallObjectMethod(Class, getNameMethod);
  991. checkException();
  992. const char *nameText = JNIenv->GetStringUTFChars(name, NULL);
  993. VStringBuffer message("javaembed: no suitable constructor for field %s", nameText);
  994. JNIenv->ReleaseStringUTFChars(name, nameText);
  995. rtlFail(0, message.str());
  996. }
  997. }
  998. jmethodID constructor;
  999. };
  1000. //----------------------------------------------------------------------
  1001. // Wrap an IRowStream into a Java Iterator
  1002. class ECLDatasetIterator : public CInterfaceOf<IInterface>
  1003. {
  1004. public:
  1005. ECLDatasetIterator(JNIEnv *JNIenv, const RtlTypeInfo *_typeInfo, const char *className, IRowStream * _val)
  1006. : typeInfo(_typeInfo), val(_val),
  1007. dummyField("<row>", NULL, typeInfo),
  1008. javaBuilder(JNIenv, &dummyField, className)
  1009. {
  1010. nextRead = false;
  1011. nextPending = NULL;
  1012. }
  1013. bool hasNext()
  1014. {
  1015. if (!nextRead)
  1016. {
  1017. nextPending = (const byte *) val->ungroupedNextRow();
  1018. nextRead = true;
  1019. if (!nextPending)
  1020. val->stop();
  1021. }
  1022. return nextPending != NULL;
  1023. }
  1024. jobject next()
  1025. {
  1026. if (!hasNext())
  1027. return NULL;
  1028. typeInfo->process(nextPending, nextPending, &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  1029. nextRead = false;
  1030. return javaBuilder.getObject();
  1031. }
  1032. protected:
  1033. const RtlTypeInfo *typeInfo; // Not linked (or linkable)
  1034. Linked<IRowStream> val;
  1035. RtlFieldStrInfo dummyField;
  1036. JavaObjectBuilder javaBuilder;
  1037. const byte *nextPending;
  1038. bool nextRead;
  1039. };
  1040. //-------------------------------------------
  1041. // A Java function that returns a dataset will return a JavaRowStream object that can be
  1042. // interrogated to return each row of the result in turn
  1043. // Note that we can't cache the JNIEnv here - calls may be made on different threads (though not at the same time).
  1044. static JNIEnv *queryJNIEnv();
  1045. class JavaRowStream : public CInterfaceOf<IRowStream>
  1046. {
  1047. public:
  1048. JavaRowStream(jobject _iterator, IEngineRowAllocator *_resultAllocator)
  1049. : resultAllocator(_resultAllocator)
  1050. {
  1051. iterator = queryJNIEnv()->NewGlobalRef(_iterator);
  1052. }
  1053. virtual const void *nextRow()
  1054. {
  1055. if (!iterator)
  1056. return NULL;
  1057. JNIEnv *JNIenv = queryJNIEnv();
  1058. // Java code would be
  1059. // if (!iterator.hasNext)
  1060. // {
  1061. // stop();
  1062. // return NULL;
  1063. // }
  1064. // result = iterator.next();
  1065. jclass iterClass =JNIenv->GetObjectClass(iterator);
  1066. javaembed::checkException(JNIenv);
  1067. jmethodID hasNextMethod = JNIenv->GetMethodID(iterClass, "hasNext", "()Z" );
  1068. javaembed::checkException(JNIenv);
  1069. jboolean hasNext = JNIenv->CallBooleanMethod(iterator, hasNextMethod);
  1070. if (!hasNext)
  1071. {
  1072. stop();
  1073. return NULL;
  1074. }
  1075. jmethodID nextMethod = JNIenv->GetMethodID(iterClass, "next", "()Ljava/lang/Object;" );
  1076. javaembed::checkException(JNIenv);
  1077. jobject result = JNIenv->CallObjectMethod(iterator, nextMethod);
  1078. RtlDynamicRowBuilder rowBuilder(resultAllocator);
  1079. const RtlTypeInfo *typeInfo = resultAllocator->queryOutputMeta()->queryTypeInfo();
  1080. assertex(typeInfo);
  1081. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  1082. JavaRowBuilder javaRowBuilder(queryJNIEnv(), &dummyField, result);
  1083. size32_t len = typeInfo->build(rowBuilder, 0, &dummyField, javaRowBuilder);
  1084. return rowBuilder.finalizeRowClear(len);
  1085. }
  1086. virtual void stop()
  1087. {
  1088. resultAllocator.clear();
  1089. if (iterator)
  1090. {
  1091. queryJNIEnv()->DeleteGlobalRef(iterator);
  1092. iterator = NULL;
  1093. }
  1094. }
  1095. protected:
  1096. Linked<IEngineRowAllocator> resultAllocator;
  1097. jobject iterator;;
  1098. };
  1099. //-------------------------------------------
  1100. // There is a singleton JavaThreadContext per thread. This allows us to
  1101. // ensure that we can make repeated calls to a Java function efficiently.
  1102. class JavaThreadContext
  1103. {
  1104. public:
  1105. JNIEnv *JNIenv; /* receives pointer to native method interface */
  1106. public:
  1107. JavaThreadContext()
  1108. {
  1109. jint res = globalState->javaVM->AttachCurrentThread((void **) &JNIenv, NULL);
  1110. assertex(res >= 0);
  1111. javaClass = NULL;
  1112. javaMethodID = NULL;
  1113. prevClassPath.set("dummy"); // Forces the call below to actually do something...
  1114. setThreadClassLoader("");
  1115. }
  1116. ~JavaThreadContext()
  1117. {
  1118. if (javaClass)
  1119. JNIenv->DeleteGlobalRef(javaClass);
  1120. // According to the Java VM 1.7 docs, "A native thread attached to
  1121. // the VM must call DetachCurrentThread() to detach itself before
  1122. // exiting."
  1123. globalState->javaVM->DetachCurrentThread();
  1124. }
  1125. void checkException()
  1126. {
  1127. if (JNIenv->ExceptionCheck())
  1128. {
  1129. jthrowable exception = JNIenv->ExceptionOccurred();
  1130. JNIenv->ExceptionClear();
  1131. jclass throwableClass = JNIenv->FindClass("java/lang/Throwable");
  1132. jmethodID throwableToString = JNIenv->GetMethodID(throwableClass, "toString", "()Ljava/lang/String;");
  1133. jstring cause = (jstring) JNIenv->CallObjectMethod(exception, throwableToString);
  1134. const char *text = JNIenv->GetStringUTFChars(cause, 0);
  1135. VStringBuffer message("javaembed: In method %s: %s", prevtext.get(), text);
  1136. JNIenv->ReleaseStringUTFChars(cause, text);
  1137. rtlFail(0, message.str());
  1138. }
  1139. }
  1140. jobject getSystemClassLoader()
  1141. {
  1142. JNIenv->ExceptionClear();
  1143. jclass javaLangClassLoaderClass = JNIenv->FindClass("java/lang/ClassLoader");
  1144. checkException();
  1145. jmethodID getSystemClassLoaderMethod = JNIenv->GetStaticMethodID(javaLangClassLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
  1146. checkException();
  1147. jobject systemClassLoaderObj = JNIenv->CallStaticObjectMethod(javaLangClassLoaderClass, getSystemClassLoaderMethod);
  1148. checkException();
  1149. assertex(systemClassLoaderObj);
  1150. return systemClassLoaderObj;
  1151. }
  1152. void setThreadClassLoader(jobject classLoader)
  1153. {
  1154. JNIenv->ExceptionClear();
  1155. jclass javaLangThreadClass = JNIenv->FindClass("java/lang/Thread");
  1156. checkException();
  1157. jmethodID currentThreadMethod = JNIenv->GetStaticMethodID(javaLangThreadClass, "currentThread", "()Ljava/lang/Thread;");
  1158. checkException();
  1159. jobject threadObj = JNIenv->CallStaticObjectMethod(javaLangThreadClass, currentThreadMethod);
  1160. checkException();
  1161. jmethodID setContextClassLoaderMethod = JNIenv->GetMethodID(javaLangThreadClass, "setContextClassLoader", "(Ljava/lang/ClassLoader;)V");
  1162. checkException();
  1163. JNIenv->CallObjectMethod(threadObj, setContextClassLoaderMethod, classLoader);
  1164. checkException();
  1165. }
  1166. jobject getThreadClassLoader()
  1167. {
  1168. JNIenv->ExceptionClear();
  1169. jclass javaLangThreadClass = JNIenv->FindClass("java/lang/Thread");
  1170. checkException();
  1171. jmethodID currentThreadMethod = JNIenv->GetStaticMethodID(javaLangThreadClass, "currentThread", "()Ljava/lang/Thread;");
  1172. checkException();
  1173. jobject threadObj = JNIenv->CallStaticObjectMethod(javaLangThreadClass, currentThreadMethod);
  1174. checkException();
  1175. jmethodID getContextClassLoaderMethod = JNIenv->GetMethodID(javaLangThreadClass, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
  1176. checkException();
  1177. jobject contextClassLoaderObj = JNIenv->CallObjectMethod(threadObj, getContextClassLoaderMethod);
  1178. checkException();
  1179. assertex(contextClassLoaderObj);
  1180. return contextClassLoaderObj;
  1181. }
  1182. void setThreadClassLoader(const char *classPath)
  1183. {
  1184. if (classPath && *classPath)
  1185. {
  1186. if (prevClassPath && strcmp(classPath, prevClassPath) == 0)
  1187. return;
  1188. jclass URLcls = JNIenv->FindClass("java/net/URL");
  1189. checkException();
  1190. jmethodID URLclsMid = JNIenv->GetMethodID(URLcls, "<init>","(Ljava/lang/String;)V");
  1191. checkException();
  1192. StringArray paths;
  1193. paths.appendList(classPath, ";"); // NOTE - as we need to be able to include : in the urls, we can't use ENVSEP here
  1194. jobjectArray URLArray = JNIenv->NewObjectArray(paths.length(), URLcls, NULL);
  1195. ForEachItemIn(idx, paths)
  1196. {
  1197. StringBuffer usepath;
  1198. const char *path = paths.item(idx);
  1199. if (!strchr(path, ':'))
  1200. usepath.append("file:");
  1201. usepath.append(path);
  1202. jstring jstr = JNIenv->NewStringUTF(usepath.str());
  1203. checkException();
  1204. jobject URLobj = JNIenv->NewObject(URLcls, URLclsMid, jstr);
  1205. checkException();
  1206. JNIenv->SetObjectArrayElement(URLArray, idx, URLobj);
  1207. JNIenv->DeleteLocalRef(URLobj);
  1208. JNIenv->DeleteLocalRef(jstr);
  1209. }
  1210. checkException();
  1211. jclass customLoaderClass = JNIenv->FindClass("java/net/URLClassLoader");
  1212. checkException();
  1213. jmethodID newInstance = JNIenv->GetStaticMethodID(customLoaderClass, "newInstance","([Ljava/net/URL;Ljava/lang/ClassLoader;)Ljava/net/URLClassLoader;");
  1214. checkException();
  1215. jobject contextClassLoaderObj = JNIenv->NewGlobalRef(JNIenv->CallStaticObjectMethod(customLoaderClass, newInstance, URLArray, getSystemClassLoader()));
  1216. checkException();
  1217. assertex(contextClassLoaderObj);
  1218. setThreadClassLoader(contextClassLoaderObj);
  1219. prevClassPath.set(classPath);
  1220. }
  1221. else
  1222. {
  1223. if (prevClassPath)
  1224. setThreadClassLoader(getSystemClassLoader());
  1225. prevClassPath.clear();
  1226. }
  1227. }
  1228. inline void importFunction(size32_t lenChars, const char *utf, const char *options)
  1229. {
  1230. size32_t bytes = rtlUtf8Size(lenChars, utf);
  1231. StringBuffer text(bytes, utf);
  1232. setThreadClassLoader(options);
  1233. if (!prevtext || strcmp(text, prevtext) != 0)
  1234. {
  1235. prevtext.clear();
  1236. // Name should be in the form class.method:signature
  1237. const char *funcname = strchr(text, '.');
  1238. if (!funcname)
  1239. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid import name %s - Expected classname.methodname:signature", text.str());
  1240. const char *signature = strchr(funcname, ':');
  1241. if (!signature)
  1242. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid import name %s - Expected classname.methodname:signature", text.str());
  1243. StringBuffer classname(funcname-text, text);
  1244. funcname++; // skip the '.'
  1245. StringBuffer methodname(signature-funcname, funcname);
  1246. signature++; // skip the ':'
  1247. // We need to patch up the provided signature - any instances of <classname; need to be replaced by Ljava.utils.iterator
  1248. StringBuffer javaSignature;
  1249. const char *finger = signature;
  1250. while (*finger)
  1251. {
  1252. if (*finger == '<')
  1253. {
  1254. javaSignature.append("Ljava/util/Iterator;");
  1255. finger = strchr(finger, ';');
  1256. if (!finger)
  1257. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid java function signature %s", signature);
  1258. }
  1259. else
  1260. javaSignature.append(*finger);
  1261. finger++;
  1262. }
  1263. if (javaClass)
  1264. JNIenv->DeleteGlobalRef(javaClass);
  1265. jobject classLoader = getThreadClassLoader();
  1266. jmethodID loadClassMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(classLoader), "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
  1267. jstring methodString = JNIenv->NewStringUTF(classname);
  1268. javaClass = (jclass) JNIenv->NewGlobalRef(JNIenv->CallObjectMethod(classLoader, loadClassMethod, methodString));
  1269. if (!javaClass)
  1270. throw MakeStringException(MSGAUD_user, 0, "javaembed: Failed to resolve class name %s", classname.str());
  1271. javaMethodID = JNIenv->GetStaticMethodID(javaClass, methodname, javaSignature);
  1272. if (!javaMethodID)
  1273. throw MakeStringException(MSGAUD_user, 0, "javaembed: Failed to resolve method name %s with signature %s", methodname.str(), signature);
  1274. const char *returnSig = strrchr(signature, ')');
  1275. assertex(returnSig); // Otherwise how did Java accept it??
  1276. returnSig++;
  1277. returnType.set(returnSig);
  1278. argsig.set(signature);
  1279. prevtext.set(text);
  1280. }
  1281. }
  1282. inline void callFunction(jvalue &result, const jvalue * args)
  1283. {
  1284. JNIenv->ExceptionClear();
  1285. switch (returnType.get()[0])
  1286. {
  1287. case 'C': result.c = JNIenv->CallStaticCharMethodA(javaClass, javaMethodID, args); break;
  1288. case 'Z': result.z = JNIenv->CallStaticBooleanMethodA(javaClass, javaMethodID, args); break;
  1289. case 'J': result.j = JNIenv->CallStaticLongMethodA(javaClass, javaMethodID, args); break;
  1290. case 'F': result.f = JNIenv->CallStaticFloatMethodA(javaClass, javaMethodID, args); break;
  1291. case 'D': result.d = JNIenv->CallStaticDoubleMethodA(javaClass, javaMethodID, args); break;
  1292. case 'I': result.i = JNIenv->CallStaticIntMethodA(javaClass, javaMethodID, args); break;
  1293. case 'S': result.s = JNIenv->CallStaticShortMethodA(javaClass, javaMethodID, args); break;
  1294. case 'B': result.s = JNIenv->CallStaticByteMethodA(javaClass, javaMethodID, args); break;
  1295. case '[':
  1296. case 'L': result.l = JNIenv->CallStaticObjectMethodA(javaClass, javaMethodID, args); break;
  1297. default: throwUnexpected();
  1298. }
  1299. checkException();
  1300. }
  1301. inline __int64 getSignedResult(jvalue & result)
  1302. {
  1303. switch (returnType.get()[0])
  1304. {
  1305. case 'B': return result.b;
  1306. case 'S': return result.s;
  1307. case 'I': return result.i;
  1308. case 'J': return result.j;
  1309. case 'L':
  1310. {
  1311. // Result should be of class 'Number'
  1312. if (!result.l)
  1313. return 0;
  1314. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "longValue", "()J");
  1315. if (!getVal)
  1316. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1317. return JNIenv->CallLongMethod(result.l, getVal);
  1318. }
  1319. default:
  1320. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1321. }
  1322. }
  1323. inline double getDoubleResult(jvalue &result)
  1324. {
  1325. switch (returnType.get()[0])
  1326. {
  1327. case 'D': return result.d;
  1328. case 'F': return result.f;
  1329. case 'L':
  1330. {
  1331. // Result should be of class 'Number'
  1332. if (!result.l)
  1333. return 0;
  1334. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "doubleValue", "()D");
  1335. if (!getVal)
  1336. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1337. return JNIenv->CallDoubleMethod(result.l, getVal);
  1338. }
  1339. default:
  1340. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1341. }
  1342. }
  1343. bool getBooleanResult(jvalue &result)
  1344. {
  1345. switch (returnType.get()[0])
  1346. {
  1347. case 'Z': return result.z;
  1348. case 'L':
  1349. {
  1350. // Result should be of class 'Boolean'
  1351. if (!result.l)
  1352. return false;
  1353. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "booleanValue", "()Z");
  1354. if (!getVal)
  1355. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1356. return JNIenv->CallBooleanMethod(result.l, getVal);
  1357. }
  1358. default:
  1359. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1360. }
  1361. }
  1362. inline void getDataResult(jvalue &result, size32_t &__len, void * &__result)
  1363. {
  1364. if (strcmp(returnType, "[B")!=0)
  1365. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1366. jbyteArray array = (jbyteArray) result.l;
  1367. __len = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  1368. __result = (__len > 0 ? rtlMalloc(__len) : NULL);
  1369. if (__result)
  1370. JNIenv->GetByteArrayRegion(array, 0, __len, (jbyte *) __result);
  1371. }
  1372. inline void getStringResult(jvalue &result, size32_t &__len, char * &__result)
  1373. {
  1374. switch (returnType.get()[0])
  1375. {
  1376. case 'C': // Single char returned, prototyped as STRING or STRING1 in ECL
  1377. rtlUnicodeToStrX(__len, __result, 1, &result.c);
  1378. break;
  1379. case 'L':
  1380. {
  1381. jstring sresult = (jstring) result.l;
  1382. if (sresult)
  1383. {
  1384. size_t size = JNIenv->GetStringUTFLength(sresult); // in bytes
  1385. const char *text = JNIenv->GetStringUTFChars(sresult, NULL);
  1386. size32_t chars = rtlUtf8Length(size, text);
  1387. rtlUtf8ToStrX(__len, __result, chars, text);
  1388. JNIenv->ReleaseStringUTFChars(sresult, text);
  1389. }
  1390. else
  1391. {
  1392. __len = 0;
  1393. __result = NULL;
  1394. }
  1395. break;
  1396. }
  1397. default:
  1398. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1399. }
  1400. }
  1401. inline void getUTF8Result(jvalue &result, size32_t &__chars, char * &__result)
  1402. {
  1403. switch (returnType.get()[0])
  1404. {
  1405. case 'C': // Single jchar returned, prototyped as UTF8 in ECL
  1406. rtlUnicodeToUtf8X(__chars, __result, 1, &result.c);
  1407. break;
  1408. case 'L':
  1409. {
  1410. jstring sresult = (jstring) result.l;
  1411. if (sresult)
  1412. {
  1413. size_t size = JNIenv->GetStringUTFLength(sresult); // Returns length in bytes (not chars)
  1414. const char * text = JNIenv->GetStringUTFChars(sresult, NULL);
  1415. rtlUtf8ToUtf8X(__chars, __result, rtlUtf8Length(size, text), text);
  1416. JNIenv->ReleaseStringUTFChars(sresult, text);
  1417. }
  1418. else
  1419. {
  1420. __chars = 0;
  1421. __result = NULL;
  1422. }
  1423. break;
  1424. }
  1425. default:
  1426. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1427. }
  1428. }
  1429. inline void getUnicodeResult(jvalue &result, size32_t &__chars, UChar * &__result)
  1430. {
  1431. switch (returnType.get()[0])
  1432. {
  1433. case 'C': // Single jchar returned, prototyped as UNICODE or UNICODE1 in ECL
  1434. rtlUnicodeToUnicodeX(__chars, __result, 1, &result.c);
  1435. break;
  1436. case 'L':
  1437. {
  1438. jstring sresult = (jstring) result.l;
  1439. if (sresult)
  1440. {
  1441. size_t size = JNIenv->GetStringUTFLength(sresult); // in bytes
  1442. const char *text = JNIenv->GetStringUTFChars(sresult, NULL);
  1443. size32_t chars = rtlUtf8Length(size, text);
  1444. rtlUtf8ToUnicodeX(__chars, __result, chars, text);
  1445. JNIenv->ReleaseStringUTFChars(sresult, text);
  1446. }
  1447. else
  1448. {
  1449. __chars = 0;
  1450. __result = NULL;
  1451. }
  1452. break;
  1453. }
  1454. default:
  1455. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1456. }
  1457. }
  1458. inline void getSetResult(jvalue &result, bool & __isAllResult, size32_t & __resultBytes, void * & __result, int _elemType, size32_t elemSize)
  1459. {
  1460. if (returnType.get()[0] != '[')
  1461. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result (array expected)");
  1462. type_t elemType = (type_t) _elemType;
  1463. jarray array = (jarray) result.l;
  1464. int numResults = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  1465. rtlRowBuilder out;
  1466. byte *outData = NULL;
  1467. size32_t outBytes = 0;
  1468. if (numResults > 0)
  1469. {
  1470. if (elemSize != UNKNOWN_LENGTH)
  1471. {
  1472. out.ensureAvailable(numResults * elemSize); // MORE - check for overflow?
  1473. outData = out.getbytes();
  1474. }
  1475. switch(returnType.get()[1])
  1476. {
  1477. case 'Z':
  1478. checkType(type_boolean, sizeof(jboolean), elemType, elemSize);
  1479. JNIenv->GetBooleanArrayRegion((jbooleanArray) array, 0, numResults, (jboolean *) outData);
  1480. break;
  1481. case 'B':
  1482. checkType(type_int, sizeof(jbyte), elemType, elemSize);
  1483. JNIenv->GetByteArrayRegion((jbyteArray) array, 0, numResults, (jbyte *) outData);
  1484. break;
  1485. case 'C':
  1486. // we COULD map to a set of string1, but is there any point?
  1487. throw MakeStringException(0, "javaembed: Return type mismatch (char[] not supported)");
  1488. break;
  1489. case 'S':
  1490. checkType(type_int, sizeof(jshort), elemType, elemSize);
  1491. JNIenv->GetShortArrayRegion((jshortArray) array, 0, numResults, (jshort *) outData);
  1492. break;
  1493. case 'I':
  1494. checkType(type_int, sizeof(jint), elemType, elemSize);
  1495. JNIenv->GetIntArrayRegion((jintArray) array, 0, numResults, (jint *) outData);
  1496. break;
  1497. case 'J':
  1498. checkType(type_int, sizeof(jlong), elemType, elemSize);
  1499. JNIenv->GetLongArrayRegion((jlongArray) array, 0, numResults, (jlong *) outData);
  1500. break;
  1501. case 'F':
  1502. checkType(type_real, sizeof(jfloat), elemType, elemSize);
  1503. JNIenv->GetFloatArrayRegion((jfloatArray) array, 0, numResults, (jfloat *) outData);
  1504. break;
  1505. case 'D':
  1506. checkType(type_real, sizeof(jdouble), elemType, elemSize);
  1507. JNIenv->GetDoubleArrayRegion((jdoubleArray) array, 0, numResults, (jdouble *) outData);
  1508. break;
  1509. case 'L':
  1510. if (strcmp(returnType, "[Ljava/lang/String;") == 0)
  1511. {
  1512. for (int i = 0; i < numResults; i++)
  1513. {
  1514. jstring elem = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) array, i);
  1515. size_t lenBytes = JNIenv->GetStringUTFLength(elem); // in bytes
  1516. const char *text = JNIenv->GetStringUTFChars(elem, NULL);
  1517. switch (elemType)
  1518. {
  1519. case type_string:
  1520. if (elemSize == UNKNOWN_LENGTH)
  1521. {
  1522. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  1523. outData = out.getbytes() + outBytes;
  1524. * (size32_t *) outData = lenBytes;
  1525. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  1526. outBytes += lenBytes + sizeof(size32_t);
  1527. }
  1528. else
  1529. rtlStrToStr(elemSize, outData, lenBytes, text);
  1530. break;
  1531. case type_varstring:
  1532. if (elemSize == UNKNOWN_LENGTH)
  1533. {
  1534. out.ensureAvailable(outBytes + lenBytes + 1);
  1535. outData = out.getbytes() + outBytes;
  1536. rtlStrToVStr(0, outData, lenBytes, text);
  1537. outBytes += lenBytes + 1;
  1538. }
  1539. else
  1540. rtlStrToVStr(elemSize, outData, lenBytes, text); // Fixed size null terminated strings... weird.
  1541. break;
  1542. case type_utf8:
  1543. case type_unicode:
  1544. {
  1545. size32_t numchars = rtlUtf8Length(lenBytes, text);
  1546. if (elemType == type_utf8)
  1547. {
  1548. assertex (elemSize == UNKNOWN_LENGTH);
  1549. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  1550. outData = out.getbytes() + outBytes;
  1551. * (size32_t *) outData = numchars;
  1552. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  1553. outBytes += lenBytes + sizeof(size32_t);
  1554. }
  1555. else
  1556. {
  1557. if (elemSize == UNKNOWN_LENGTH)
  1558. {
  1559. // You can't assume that number of chars in utf8 matches number in unicode16 ...
  1560. size32_t numchars16;
  1561. rtlDataAttr unicode16;
  1562. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  1563. out.ensureAvailable(outBytes + numchars16*sizeof(UChar) + sizeof(size32_t));
  1564. outData = out.getbytes() + outBytes;
  1565. * (size32_t *) outData = numchars16;
  1566. rtlUnicodeToUnicode(numchars16, (UChar *) (outData+sizeof(size32_t)), numchars16, unicode16.getustr());
  1567. outBytes += numchars16*sizeof(UChar) + sizeof(size32_t);
  1568. }
  1569. else
  1570. rtlUtf8ToUnicode(elemSize / sizeof(UChar), (UChar *) outData, numchars, text);
  1571. }
  1572. break;
  1573. }
  1574. default:
  1575. JNIenv->ReleaseStringUTFChars(elem, text);
  1576. throw MakeStringException(0, "javaembed: Return type mismatch (ECL string type expected)");
  1577. }
  1578. JNIenv->ReleaseStringUTFChars(elem, text);
  1579. JNIenv->DeleteLocalRef(elem);
  1580. if (elemSize != UNKNOWN_LENGTH)
  1581. outData += elemSize;
  1582. }
  1583. }
  1584. else
  1585. throw MakeStringException(0, "javaembed: Return type mismatch (%s[] not supported)", returnType.get()+2);
  1586. break;
  1587. }
  1588. }
  1589. __isAllResult = false;
  1590. __resultBytes = elemSize == UNKNOWN_LENGTH ? outBytes : elemSize * numResults;
  1591. __result = out.detachdata();
  1592. }
  1593. inline const char *querySignature()
  1594. {
  1595. return argsig.get();
  1596. }
  1597. size32_t getRowResult(jobject result, ARowBuilder &builder)
  1598. {
  1599. const RtlTypeInfo *typeInfo = builder.queryAllocator()->queryOutputMeta()->queryTypeInfo();
  1600. assertex(typeInfo);
  1601. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  1602. JavaRowBuilder javaRowBuilder(JNIenv, &dummyField, result);
  1603. return typeInfo->build(builder, 0, &dummyField, javaRowBuilder);
  1604. }
  1605. private:
  1606. StringAttr returnType;
  1607. StringAttr argsig;
  1608. StringAttr prevtext;
  1609. StringAttr prevClassPath;
  1610. jclass javaClass;
  1611. jmethodID javaMethodID;
  1612. };
  1613. // Each call to a Java function will use a new JavaEmbedScriptContext object
  1614. #define MAX_JNI_ARGS 10
  1615. class JavaEmbedImportContext : public CInterfaceOf<IEmbedFunctionContext>
  1616. {
  1617. public:
  1618. JavaEmbedImportContext(JavaThreadContext *_sharedCtx, const char *options)
  1619. : sharedCtx(_sharedCtx)
  1620. {
  1621. argcount = 0;
  1622. argsig = NULL;
  1623. StringArray opts;
  1624. opts.appendList(options, ",");
  1625. ForEachItemIn(idx, opts)
  1626. {
  1627. const char *opt = opts.item(idx);
  1628. const char *val = strchr(opt, '=');
  1629. if (val)
  1630. {
  1631. StringBuffer optName(val-opt, opt);
  1632. val++;
  1633. if (stricmp(optName, "classpath")==0)
  1634. classpath.set(val);
  1635. else
  1636. throw MakeStringException(0, "javaembed: Unknown option %s", optName.str());
  1637. }
  1638. }
  1639. // Create a new frame for local references and increase the capacity
  1640. // of those references to 64 (default is 16)
  1641. sharedCtx->JNIenv->PushLocalFrame(64);
  1642. }
  1643. ~JavaEmbedImportContext()
  1644. {
  1645. // Pop local reference frame; explicitly frees all local
  1646. // references made during that frame's lifetime
  1647. sharedCtx->JNIenv->PopLocalFrame(NULL);
  1648. }
  1649. virtual bool getBooleanResult()
  1650. {
  1651. return sharedCtx->getBooleanResult(result);
  1652. }
  1653. virtual void getDataResult(size32_t &__len, void * &__result)
  1654. {
  1655. sharedCtx->getDataResult(result, __len, __result);
  1656. }
  1657. virtual double getRealResult()
  1658. {
  1659. return sharedCtx->getDoubleResult(result);
  1660. }
  1661. virtual __int64 getSignedResult()
  1662. {
  1663. return sharedCtx->getSignedResult(result);
  1664. }
  1665. virtual unsigned __int64 getUnsignedResult()
  1666. {
  1667. throw MakeStringException(MSGAUD_user, 0, "javaembed: Unsigned results not supported"); // Java doesn't support unsigned
  1668. }
  1669. virtual void getStringResult(size32_t &__len, char * &__result)
  1670. {
  1671. sharedCtx->getStringResult(result, __len, __result);
  1672. }
  1673. virtual void getUTF8Result(size32_t &__chars, char * &__result)
  1674. {
  1675. sharedCtx->getUTF8Result(result, __chars, __result);
  1676. }
  1677. virtual void getUnicodeResult(size32_t &__chars, UChar * &__result)
  1678. {
  1679. sharedCtx->getUnicodeResult(result, __chars, __result);
  1680. }
  1681. virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
  1682. {
  1683. sharedCtx->getSetResult(result, __isAllResult, __resultBytes, __result, elemType, elemSize);
  1684. }
  1685. virtual IRowStream *getDatasetResult(IEngineRowAllocator * _resultAllocator)
  1686. {
  1687. return new JavaRowStream(result.l, _resultAllocator);
  1688. }
  1689. virtual byte * getRowResult(IEngineRowAllocator * _resultAllocator)
  1690. {
  1691. RtlDynamicRowBuilder rowBuilder(_resultAllocator);
  1692. size32_t len = sharedCtx->getRowResult(result.l, rowBuilder);
  1693. return (byte *) rowBuilder.finalizeRowClear(len);
  1694. }
  1695. virtual size32_t getTransformResult(ARowBuilder & builder)
  1696. {
  1697. return sharedCtx->getRowResult(result.l, builder);
  1698. }
  1699. virtual void bindBooleanParam(const char *name, bool val)
  1700. {
  1701. if (*argsig != 'Z')
  1702. typeError("BOOLEAN");
  1703. argsig++;
  1704. jvalue v;
  1705. v.z = val;
  1706. addArg(v);
  1707. }
  1708. virtual void bindDataParam(const char *name, size32_t len, const void *val)
  1709. {
  1710. if (argsig[0] != '[' || argsig[1] != 'B')
  1711. typeError("DATA");
  1712. argsig += 2;
  1713. jvalue v;
  1714. jbyteArray javaData = sharedCtx->JNIenv->NewByteArray(len);
  1715. sharedCtx->JNIenv->SetByteArrayRegion(javaData, 0, len, (jbyte *) val);
  1716. v.l = javaData;
  1717. addArg(v);
  1718. }
  1719. virtual void bindRealParam(const char *name, double val)
  1720. {
  1721. jvalue v;
  1722. switch(*argsig)
  1723. {
  1724. case 'D':
  1725. v.d = val;
  1726. break;
  1727. case 'F':
  1728. v.f = val;
  1729. break;
  1730. default:
  1731. typeError("REAL");
  1732. break;
  1733. }
  1734. argsig++;
  1735. addArg(v);
  1736. }
  1737. virtual void bindSignedParam(const char *name, __int64 val)
  1738. {
  1739. jvalue v;
  1740. switch(*argsig)
  1741. {
  1742. case 'I':
  1743. v.i = val;
  1744. break;
  1745. case 'J':
  1746. v.j = val;
  1747. break;
  1748. case 'S':
  1749. v.s = val;
  1750. break;
  1751. case 'B':
  1752. v.b = val;
  1753. break;
  1754. default:
  1755. typeError("INTEGER");
  1756. break;
  1757. }
  1758. argsig++;
  1759. addArg(v);
  1760. }
  1761. virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
  1762. {
  1763. throw MakeStringException(MSGAUD_user, 0, "javaembed: Unsigned parameters not supported"); // Java doesn't support unsigned
  1764. }
  1765. virtual void bindStringParam(const char *name, size32_t len, const char *val)
  1766. {
  1767. jvalue v;
  1768. switch(*argsig)
  1769. {
  1770. case 'C':
  1771. rtlStrToUnicode(1, &v.c, len, val);
  1772. argsig++;
  1773. break;
  1774. case 'L':
  1775. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  1776. {
  1777. argsig += 18;
  1778. unsigned unicodeChars;
  1779. UChar *unicode;
  1780. rtlStrToUnicodeX(unicodeChars, unicode, len, val);
  1781. v.l = sharedCtx->JNIenv->NewString(unicode, unicodeChars);
  1782. rtlFree(unicode);
  1783. break;
  1784. }
  1785. // fall into ...
  1786. default:
  1787. typeError("STRING");
  1788. break;
  1789. }
  1790. addArg(v);
  1791. }
  1792. virtual void bindVStringParam(const char *name, const char *val)
  1793. {
  1794. bindStringParam(name, strlen(val), val);
  1795. }
  1796. virtual void bindUTF8Param(const char *name, size32_t numchars, const char *val)
  1797. {
  1798. jvalue v;
  1799. switch(*argsig)
  1800. {
  1801. case 'C':
  1802. rtlUtf8ToUnicode(1, &v.c, numchars, val);
  1803. argsig++;
  1804. break;
  1805. case 'L':
  1806. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  1807. {
  1808. argsig += 18;
  1809. unsigned unicodeChars;
  1810. UChar *unicode;
  1811. rtlUtf8ToUnicodeX(unicodeChars, unicode, numchars, val);
  1812. v.l = sharedCtx->JNIenv->NewString(unicode, unicodeChars);
  1813. rtlFree(unicode);
  1814. break;
  1815. }
  1816. // fall into ...
  1817. default:
  1818. typeError("UTF8");
  1819. break;
  1820. }
  1821. addArg(v);
  1822. }
  1823. virtual void bindUnicodeParam(const char *name, size32_t numchars, const UChar *val)
  1824. {
  1825. jvalue v;
  1826. switch(*argsig)
  1827. {
  1828. case 'C':
  1829. rtlUnicodeToUnicode(1, &v.c, numchars, val);
  1830. argsig++;
  1831. break;
  1832. case 'L':
  1833. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  1834. {
  1835. argsig += 18;
  1836. v.l = sharedCtx->JNIenv->NewString(val, numchars);
  1837. break;
  1838. }
  1839. // fall into ...
  1840. default:
  1841. typeError("UNICODE");
  1842. break;
  1843. }
  1844. addArg(v);
  1845. }
  1846. virtual void bindSetParam(const char *name, int _elemType, size32_t elemSize, bool isAll, size32_t totalBytes, void *setData)
  1847. {
  1848. jvalue v;
  1849. if (*argsig != '[')
  1850. typeError("SET");
  1851. argsig++;
  1852. type_t elemType = (type_t) _elemType;
  1853. int numElems = totalBytes / elemSize;
  1854. switch(*argsig)
  1855. {
  1856. case 'Z':
  1857. checkType(type_boolean, sizeof(jboolean), elemType, elemSize);
  1858. v.l = sharedCtx->JNIenv->NewBooleanArray(numElems);
  1859. sharedCtx->JNIenv->SetBooleanArrayRegion((jbooleanArray) v.l, 0, numElems, (jboolean *) setData);
  1860. break;
  1861. case 'B':
  1862. checkType(type_int, sizeof(jbyte), elemType, elemSize);
  1863. v.l = sharedCtx->JNIenv->NewByteArray(numElems);
  1864. sharedCtx->JNIenv->SetByteArrayRegion((jbyteArray) v.l, 0, numElems, (jbyte *) setData);
  1865. break;
  1866. case 'C':
  1867. // we COULD map to a set of string1, but is there any point?
  1868. typeError("");
  1869. break;
  1870. case 'S':
  1871. checkType(type_int, sizeof(jshort), elemType, elemSize);
  1872. v.l = sharedCtx->JNIenv->NewShortArray(numElems);
  1873. sharedCtx->JNIenv->SetShortArrayRegion((jshortArray) v.l, 0, numElems, (jshort *) setData);
  1874. break;
  1875. case 'I':
  1876. checkType(type_int, sizeof(jint), elemType, elemSize);
  1877. v.l = sharedCtx->JNIenv->NewIntArray(numElems);
  1878. sharedCtx->JNIenv->SetIntArrayRegion((jintArray) v.l, 0, numElems, (jint *) setData);
  1879. break;
  1880. case 'J':
  1881. checkType(type_int, sizeof(jlong), elemType, elemSize);
  1882. v.l = sharedCtx->JNIenv->NewLongArray(numElems);
  1883. sharedCtx->JNIenv->SetLongArrayRegion((jlongArray) v.l, 0, numElems, (jlong *) setData);
  1884. break;
  1885. case 'F':
  1886. checkType(type_real, sizeof(jfloat), elemType, elemSize);
  1887. v.l = sharedCtx->JNIenv->NewFloatArray(numElems);
  1888. sharedCtx->JNIenv->SetFloatArrayRegion((jfloatArray) v.l, 0, numElems, (jfloat *) setData);
  1889. break;
  1890. case 'D':
  1891. checkType(type_real, sizeof(jdouble), elemType, elemSize);
  1892. v.l = sharedCtx->JNIenv->NewDoubleArray(numElems);
  1893. sharedCtx->JNIenv->SetDoubleArrayRegion((jdoubleArray) v.l, 0, numElems, (jdouble *) setData);
  1894. break;
  1895. case 'L':
  1896. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  1897. {
  1898. argsig += 17; // Yes, 17, because we increment again at the end of the case
  1899. const byte *inData = (const byte *) setData;
  1900. const byte *endData = inData + totalBytes;
  1901. if (elemSize == UNKNOWN_LENGTH)
  1902. {
  1903. numElems = 0;
  1904. // Will need 2 passes to work out how many elements there are in the set :(
  1905. while (inData < endData)
  1906. {
  1907. int thisSize;
  1908. switch (elemType)
  1909. {
  1910. case type_varstring:
  1911. thisSize = strlen((const char *) inData) + 1;
  1912. break;
  1913. case type_string:
  1914. thisSize = * (size32_t *) inData + sizeof(size32_t);
  1915. break;
  1916. case type_unicode:
  1917. thisSize = (* (size32_t *) inData) * sizeof(UChar) + sizeof(size32_t);
  1918. break;
  1919. case type_utf8:
  1920. thisSize = rtlUtf8Size(* (size32_t *) inData, inData + sizeof(size32_t)) + sizeof(size32_t);;
  1921. break;
  1922. default:
  1923. typeError("STRING");
  1924. }
  1925. inData += thisSize;
  1926. numElems++;
  1927. }
  1928. inData = (const byte *) setData;
  1929. }
  1930. int idx = 0;
  1931. v.l = sharedCtx->JNIenv->NewObjectArray(numElems, sharedCtx->JNIenv->FindClass("java/lang/String"), NULL);
  1932. while (inData < endData)
  1933. {
  1934. jstring thisElem;
  1935. size32_t thisSize = elemSize;
  1936. switch (elemType)
  1937. {
  1938. case type_varstring:
  1939. {
  1940. size32_t numChars = strlen((const char *) inData);
  1941. unsigned unicodeChars;
  1942. rtlDataAttr unicode;
  1943. rtlStrToUnicodeX(unicodeChars, unicode.refustr(), numChars, (const char *) inData);
  1944. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  1945. if (elemSize == UNKNOWN_LENGTH)
  1946. thisSize = numChars + 1;
  1947. break;
  1948. }
  1949. case type_string:
  1950. {
  1951. if (elemSize == UNKNOWN_LENGTH)
  1952. {
  1953. thisSize = * (size32_t *) inData;
  1954. inData += sizeof(size32_t);
  1955. }
  1956. unsigned unicodeChars;
  1957. rtlDataAttr unicode;
  1958. rtlStrToUnicodeX(unicodeChars, unicode.refustr(), thisSize, (const char *) inData);
  1959. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  1960. break;
  1961. }
  1962. case type_unicode:
  1963. {
  1964. if (elemSize == UNKNOWN_LENGTH)
  1965. {
  1966. thisSize = (* (size32_t *) inData) * sizeof(UChar); // NOTE - it's in chars...
  1967. inData += sizeof(size32_t);
  1968. }
  1969. thisElem = sharedCtx->JNIenv->NewString((const UChar *) inData, thisSize / sizeof(UChar));
  1970. //checkJPythonError();
  1971. break;
  1972. }
  1973. case type_utf8:
  1974. {
  1975. assertex (elemSize == UNKNOWN_LENGTH);
  1976. size32_t numChars = * (size32_t *) inData;
  1977. inData += sizeof(size32_t);
  1978. unsigned unicodeChars;
  1979. rtlDataAttr unicode;
  1980. rtlUtf8ToUnicodeX(unicodeChars, unicode.refustr(), numChars, (const char *) inData);
  1981. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  1982. thisSize = rtlUtf8Size(numChars, inData);
  1983. break;
  1984. }
  1985. default:
  1986. typeError("STRING");
  1987. }
  1988. sharedCtx->checkException();
  1989. inData += thisSize;
  1990. sharedCtx->JNIenv->SetObjectArrayElement((jobjectArray) v.l, idx, thisElem);
  1991. sharedCtx->JNIenv->DeleteLocalRef(thisElem);
  1992. idx++;
  1993. }
  1994. }
  1995. else
  1996. typeError("");
  1997. break;
  1998. default:
  1999. throwUnexpected();
  2000. }
  2001. argsig++;
  2002. addArg(v);
  2003. }
  2004. virtual void bindRowParam(const char *name, IOutputMetaData & metaVal, byte *val)
  2005. {
  2006. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  2007. typeError("RECORD");
  2008. // Class name is from the char after the L up to the first ;
  2009. const char *tail = strchr(argsig, ';');
  2010. if (!tail)
  2011. typeError("RECORD");
  2012. StringAttr className(argsig+1, tail - (argsig+1));
  2013. argsig = tail+1;
  2014. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  2015. assertex(typeInfo);
  2016. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  2017. JavaObjectBuilder javaBuilder(sharedCtx->JNIenv, &dummyField, className);
  2018. typeInfo->process(val, val, &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  2019. jvalue v;
  2020. v.l = javaBuilder.getObject();
  2021. addArg(v);
  2022. }
  2023. virtual void bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val)
  2024. {
  2025. jvalue v;
  2026. if (*argsig == '[')
  2027. {
  2028. ++argsig;
  2029. // At present, dataset parameters have to map to arrays. May support iterators later
  2030. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  2031. typeError("DATASET");
  2032. // Class name is from the char after the L up to the first ;
  2033. const char *tail = strchr(argsig, ';');
  2034. if (!tail)
  2035. typeError("RECORD");
  2036. StringAttr className(argsig+1, tail - (argsig+1));
  2037. argsig = tail+1;
  2038. PointerArrayOf<_jobject> allRows;
  2039. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  2040. assertex(typeInfo);
  2041. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  2042. JavaObjectBuilder javaBuilder(sharedCtx->JNIenv, &dummyField, className);
  2043. for (;;)
  2044. {
  2045. roxiemem::OwnedConstRoxieRow thisRow = val->ungroupedNextRow();
  2046. if (!thisRow)
  2047. break;
  2048. const byte *brow = (const byte *) thisRow.get();
  2049. typeInfo->process(brow, brow, &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  2050. allRows.append(javaBuilder.getObject());
  2051. }
  2052. jobjectArray array = sharedCtx->JNIenv->NewObjectArray(allRows.length(), sharedCtx->JNIenv->FindClass(className), NULL);
  2053. ForEachItemIn(idx, allRows)
  2054. {
  2055. sharedCtx->JNIenv->SetObjectArrayElement(array, idx, allRows.item(idx));
  2056. }
  2057. v.l = array;
  2058. }
  2059. else if (*argsig == '<') // An extension to the Java signatures, used to indicate
  2060. // that we will pass an iterator that generates objects of the specified type
  2061. {
  2062. ++argsig;
  2063. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  2064. typeError("DATASET");
  2065. // Class name is from the char after the L up to the first ;
  2066. const char *tail = strchr(argsig, ';');
  2067. if (!tail)
  2068. typeError("RECORD");
  2069. StringAttr className(argsig+1, tail - (argsig+1));
  2070. argsig = tail+1;
  2071. // Create a java object of type com.HPCCSystems.HpccUtils - this acts as a proxy for the iterator
  2072. sharedCtx->JNIenv->ExceptionClear();
  2073. jclass proxyClass = sharedCtx->JNIenv->FindClass("com/HPCCSystems/HpccUtils");
  2074. sharedCtx->checkException();
  2075. jmethodID constructor = sharedCtx->JNIenv->GetMethodID(proxyClass, "<init>", "(JLjava/lang/String;)V");
  2076. sharedCtx->checkException();
  2077. jvalue param;
  2078. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  2079. ECLDatasetIterator *iterator = new ECLDatasetIterator(sharedCtx->JNIenv, typeInfo, className, val);
  2080. param.j = (jlong) iterator;
  2081. iterators.append(*iterator);
  2082. jobject proxy = sharedCtx->JNIenv->NewObject(proxyClass, constructor, param, sharedCtx->JNIenv->NewStringUTF(helperLibraryName));
  2083. sharedCtx->checkException();
  2084. v.l = proxy;
  2085. }
  2086. else
  2087. typeError("DATASET");
  2088. addArg(v);
  2089. }
  2090. virtual void importFunction(size32_t lenChars, const char *utf)
  2091. {
  2092. sharedCtx->importFunction(lenChars, utf, classpath);
  2093. argsig = sharedCtx->querySignature();
  2094. assertex(*argsig == '(');
  2095. argsig++;
  2096. }
  2097. virtual void callFunction()
  2098. {
  2099. sharedCtx->callFunction(result, args);
  2100. }
  2101. virtual void compileEmbeddedScript(size32_t lenChars, const char *script)
  2102. {
  2103. throwUnexpected(); // The java language helper supports only imported functions, not embedding java code in ECL.
  2104. }
  2105. protected:
  2106. JavaThreadContext *sharedCtx;
  2107. jvalue result;
  2108. StringAttr classpath;
  2109. IArrayOf<ECLDatasetIterator> iterators; // to make sure they get freed
  2110. private:
  2111. void typeError(const char *ECLtype) __attribute__((noreturn))
  2112. {
  2113. const char *javaType;
  2114. int javaLen = 0;
  2115. switch (*argsig)
  2116. {
  2117. case 'Z': javaType = "boolean"; break;
  2118. case 'B': javaType = "byte"; break;
  2119. case 'C': javaType = "char"; break;
  2120. case 'S': javaType = "short"; break;
  2121. case 'I': javaType = "int"; break;
  2122. case 'J': javaType = "long"; break;
  2123. case 'F': javaType = "float"; break;
  2124. case 'D': javaType = "double"; break;
  2125. case '[': javaType = "array"; break;
  2126. case 'L':
  2127. {
  2128. javaType = argsig+1;
  2129. const char *semi = strchr(argsig, ';');
  2130. if (semi)
  2131. javaLen = semi - javaType;
  2132. break;
  2133. }
  2134. case ')':
  2135. throw MakeStringException(0, "javaembed: Too many ECL parameters passed for Java signature %s", sharedCtx->querySignature());
  2136. default:
  2137. throw MakeStringException(0, "javaembed: Unrecognized character %c in java signature %s", *argsig, sharedCtx->querySignature());
  2138. }
  2139. if (!javaLen)
  2140. javaLen = strlen(argsig);
  2141. throw MakeStringException(0, "javaembed: ECL type %s cannot be passed to Java type %.*s", ECLtype, javaLen, javaType);
  2142. }
  2143. void addArg(jvalue &arg)
  2144. {
  2145. assertex(argcount < MAX_JNI_ARGS);
  2146. args[argcount] = arg;
  2147. argcount++;
  2148. }
  2149. jvalue args[MAX_JNI_ARGS];
  2150. int argcount;
  2151. const char *argsig;
  2152. };
  2153. static __thread JavaThreadContext* threadContext; // We reuse per thread, for speed
  2154. static __thread ThreadTermFunc threadHookChain;
  2155. static void releaseContext()
  2156. {
  2157. if (threadContext)
  2158. {
  2159. delete threadContext;
  2160. threadContext = NULL;
  2161. }
  2162. if (threadHookChain)
  2163. {
  2164. (*threadHookChain)();
  2165. threadHookChain = NULL;
  2166. }
  2167. }
  2168. static JavaThreadContext *queryContext()
  2169. {
  2170. if (!threadContext)
  2171. {
  2172. threadContext = new JavaThreadContext;
  2173. threadHookChain = addThreadTermFunc(releaseContext);
  2174. }
  2175. return threadContext;
  2176. }
  2177. static JNIEnv *queryJNIEnv()
  2178. {
  2179. return queryContext()->JNIenv;
  2180. }
  2181. class JavaEmbedContext : public CInterfaceOf<IEmbedContext>
  2182. {
  2183. public:
  2184. virtual IEmbedFunctionContext *createFunctionContext(bool isImport, const char *options)
  2185. {
  2186. assertex(isImport);
  2187. return new JavaEmbedImportContext(queryContext(), options);
  2188. }
  2189. };
  2190. extern IEmbedContext* getEmbedContext()
  2191. {
  2192. return new JavaEmbedContext;
  2193. }
  2194. } // namespace
  2195. // Callbacks from java
  2196. extern "C" {
  2197. JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1hasNext (JNIEnv *, jclass, jlong);
  2198. JNIEXPORT jobject JNICALL Java_com_HPCCSystems_HpccUtils__1next (JNIEnv *, jclass, jlong);
  2199. }
  2200. JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1hasNext (JNIEnv *JNIenv, jclass, jlong proxy)
  2201. {
  2202. try
  2203. {
  2204. javaembed::ECLDatasetIterator *e = (javaembed::ECLDatasetIterator *) proxy;
  2205. return e->hasNext();
  2206. }
  2207. catch (IException *E)
  2208. {
  2209. StringBuffer msg;
  2210. E->errorMessage(msg);
  2211. E->Release();
  2212. jclass eClass = JNIenv->FindClass("java/lang/IllegalArgumentException");
  2213. if (eClass)
  2214. JNIenv->ThrowNew(eClass, msg.str());
  2215. return false;
  2216. }
  2217. }
  2218. JNIEXPORT jobject JNICALL Java_com_HPCCSystems_HpccUtils__1next (JNIEnv *JNIenv, jclass, jlong proxy)
  2219. {
  2220. try
  2221. {
  2222. javaembed::ECLDatasetIterator *e = (javaembed::ECLDatasetIterator *) proxy;
  2223. return e->next();
  2224. }
  2225. catch (IException *E)
  2226. {
  2227. StringBuffer msg;
  2228. E->errorMessage(msg);
  2229. E->Release();
  2230. jclass eClass = JNIenv->FindClass("java/lang/IllegalArgumentException");
  2231. if (eClass)
  2232. JNIenv->ThrowNew(eClass, msg.str());
  2233. return NULL;
  2234. }
  2235. }