Browse Source

HPCC-14256 Add DynamicESDL java services support for simple arrays

Signed-off-by: Anthony Fishbeck <anthony.fishbeck@lexisnexis.com>
Anthony Fishbeck 9 years ago
parent
commit
fd3700105c

+ 2 - 0
esp/xslt/esdl2ecl.xslt

@@ -28,6 +28,8 @@
 /*** ECL Interface generated by esdl2ecl version 1.0 from </xsl:text><xsl:copy-of select="$sourceFileName"/> <xsl:text>.xml. ***/
 /*===================================================*/
 
+import $.share;
+
 </xsl:text>
 	</xsl:template>
 	<xsl:template match="expesdl">

+ 1 - 0
initfiles/examples/EsdlExample/esdl_example.esdl

@@ -28,6 +28,7 @@ ESPStruct NameInfo
 {
     string First("Joe");
     string Last("Doe");
+    ESParray<string, Alias> Aliases;
 };
 
 ESPStruct AddressInfo

+ 6 - 0
initfiles/examples/EsdlExample/javarequest.xml

@@ -5,6 +5,12 @@
    <Name>
     <First>Joe</First>
     <Last>Doe</Last>
+    <Aliases>
+      <Alias>John</Alias>
+      <Alias>James</Alias>
+      <Alias>Joseph</Alias>
+      <Alias>Jessy</Alias>
+    </Aliases>
    </Name>
    <Addresses>
     <Address>

+ 6 - 0
initfiles/examples/EsdlExample/roxierequest.xml

@@ -5,6 +5,12 @@
    <Name>
     <First>Joe</First>
     <Last>Doe</Last>
+    <Aliases>
+     <Alias>John</Alias>
+     <Alias>James</Alias>
+     <Alias>Joseph</Alias>
+     <Alias>Jessy</Alias>
+    </Aliases>
    </Name>
    <Addresses>
     <Address>

+ 11 - 0
initfiles/examples/EsdlExample/share.ecl

@@ -0,0 +1,11 @@
+export share := module
+
+export t_IntegerArrayItem := record
+    integer value { xpath('')};
+end;
+
+export t_StringArrayItem := record
+    string value { xpath(''), MAXLENGTH(8192) };
+end;
+
+end;

+ 147 - 71
plugins/javaembed/javaembed.cpp

@@ -1254,22 +1254,36 @@ public:
                 JNIenv->DeleteGlobalRef(*pClass);
         }
     }
-
-    void writeSimpleType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
+    void writeSimpleType(const char *fieldname, jobject fieldObj)
     {
-        const char *fieldname = defObject.queryName();
-        const char *javaSig = esdl2JavaSig(esdl, defObject.queryProp("type"));
-        jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, javaSig); //tbd cache this
+        jstring fieldStr = (jstring) JNIenv->CallObjectMethod(fieldObj, objToString);
+        if (!fieldStr)
+            return;
+        const char *text = JNIenv->GetStringUTFChars(fieldStr, NULL);
+        if (text)
+            writer.outputCString(text, fieldname);
+        JNIenv->DeleteLocalRef(fieldStr);
+    }
+    void writeSimpleType(jclass parentClass, jobject parentObject, const char *fieldname, const char *javaSig)
+    {
+        if (!fieldname || !*fieldname)
+            return;
+        if (!javaSig || !*javaSig)
+            return;
+        jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, javaSig);
         if (!fieldId)
             return;
         jobject fieldObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
         if (!fieldObj)
             return;
-        jstring fieldStr = (jstring) JNIenv->CallObjectMethod(fieldObj, objToString);
-        const char *text = JNIenv->GetStringUTFChars(fieldStr, NULL);
-        if (!text)
-            return;
-        writer.outputCString(text, defObject.queryName());
+        writeSimpleType(fieldname, fieldObj);
+        JNIenv->DeleteLocalRef(fieldObj);
+    }
+    void writeSimpleType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
+    {
+        const char *fieldname = defObject.queryName();
+        const char *javaSig = esdl2JavaSig(esdl, defObject.queryProp("type"));
+        writeSimpleType(parentClass, parentObject, fieldname, javaSig);
     }
     void writeEnumType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
     {
@@ -1304,48 +1318,85 @@ public:
         writeChildren(JNIenv->GetObjectClass(fieldObj), fieldObj, defStruct);
         writer.outputEndNested(fieldname);
     }
-    void writeComplexArray(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
+
+    void writeSimpleArray(jobjectArray arrayObj, jint count, const char *name, const char *item_tag)
     {
-        IEsdlDefStruct *defStruct = esdl.queryStruct(defObject.queryProp("type"));
-        if (!defStruct)
-            return;
-        const char *fieldname = defObject.queryName();
-        jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, "Ljava/util/ArrayList;");
-        if (!fieldId)
+        writer.outputBeginNested(name, true);
+        writer.outputBeginArray(item_tag);
+        for (jint i=0; i < count; i++)
+        {
+            jobject elementObj = JNIenv->GetObjectArrayElement(arrayObj, i);
+            if(JNIenv->ExceptionOccurred())
+                break;
+            writeSimpleType(item_tag, elementObj);
+            JNIenv->DeleteLocalRef(elementObj);
+        }
+        writer.outputEndArray(item_tag);
+        writer.outputEndNested(name);
+    }
+    void writeComplexArray(jobjectArray arrayObj, jint count, const char *name, const char *item_tag, const char *itemTypeName)
+    {
+        writer.outputBeginNested(name, true);
+        writer.outputBeginArray(item_tag);
+        {
+            VStringBuffer javaClassName("%s/%s", esdlService.str(), itemTypeName);
+            jclass elementClass = FindClass(javaClassName);
+            if (!elementClass)
+                return;
+            IEsdlDefStruct *defStruct = esdl.queryStruct(itemTypeName);
+            if (!defStruct)
+                return;
+            for (jint i=0; i < count; i++)
+            {
+                jobject elementObj = JNIenv->GetObjectArrayElement(arrayObj, i);
+                if(JNIenv->ExceptionOccurred())
+                    break;
+                writer.outputBeginNested(item_tag, true);
+                writeChildren(elementClass, elementObj, defStruct);
+                writer.outputEndNested(item_tag);
+                JNIenv->DeleteLocalRef(elementObj);
+            }
+        }
+        writer.outputEndArray(item_tag);
+        writer.outputEndNested(name);
+    }
+    void writeArray(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
+    {
+        const char *itemTypeName = defObject.queryProp("type");
+        if (!itemTypeName)
             return;
-        jobject arrayListObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
-        if (!arrayListObj)
+        const char *item_tag = defObject.queryProp("item_tag");
+        if (!item_tag)
             return;
+        const char *fieldname = defObject.queryName();
         jclass arrayListClass = FindClass("java/util/ArrayList");
         if (!arrayListClass)
             return;
         jmethodID toArrayMethod = JNIenv->GetMethodID(arrayListClass, "toArray", "()[Ljava/lang/Object;" );
-        javaembed::checkException(JNIenv, false);
         if (!toArrayMethod)
             return;
 
-        VStringBuffer javaClassName("%s/%s", esdlService.str(), defObject.queryProp("type"));
-        jclass elementClass = FindClass(javaClassName);
-        if (!elementClass)
+        jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, "Ljava/util/ArrayList;");
+        if (!fieldId)
             return;
-        const char *item_tag = defObject.queryProp("item_tag");
-        writer.outputBeginNested(defObject.queryName(), true);
-        writer.outputBeginArray(item_tag);
+        jobject arrayListObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
+        if (!arrayListObj)
+            return;
+        javaembed::checkException(JNIenv, false);
         jobjectArray arrayObj = (jobjectArray) JNIenv->CallObjectMethod(arrayListObj, toArrayMethod);
-        jint i;
-        jint count = JNIenv->GetArrayLength(arrayObj);
-        for (i=0; i < count; i++)
+        if (arrayObj)
         {
-            jobject elementObj = JNIenv->GetObjectArrayElement(arrayObj, i);
-            if(JNIenv->ExceptionOccurred())
-                break;
-            writer.outputBeginNested(item_tag, true);
-            writeChildren(elementClass, elementObj, defStruct);
-            writer.outputEndNested(item_tag);
-            JNIenv->DeleteLocalRef(elementObj);
+            jint count = JNIenv->GetArrayLength(arrayObj);
+            if (count)
+            {
+                if (esdl2JavaSig(esdl, itemTypeName))
+                    writeSimpleArray(arrayObj, count, defObject.queryName(), item_tag);
+                else
+                    writeComplexArray(arrayObj, count, defObject.queryName(), item_tag, itemTypeName);
+            }
+            JNIenv->DeleteLocalRef(arrayObj);
         }
-        writer.outputEndArray(item_tag);
-        writer.outputEndNested(defObject.queryName());
+        JNIenv->DeleteLocalRef(arrayListObj);
     }
     void writeChildren(jclass javaClass, jobject javaObject, IEsdlDefStruct *defStruct)
     {
@@ -1366,7 +1417,7 @@ public:
             }
             else if (child.getEsdlType()==EsdlTypeArray)
             {
-                writeComplexArray(javaClass, javaObject, child); //adf: tbd handle simple array
+                writeArray(javaClass, javaObject, child);
             }
         }
     }
@@ -2090,24 +2141,33 @@ public:
     }
     virtual void outputString(unsigned size, const char *text, const char *fieldname)
     {
-        IEsdlDefStruct *defStruct = queryCurrentEsdlStruct();
-        if (!defStruct)
-            return;
-        IEsdlDefObject *defField = defStruct->queryChild(fieldname);
-        if (!defField)
+        DefStackEntry *parent = defStack.length() ? &defStack.tos() : NULL;
+        if (!parent)
             return;
-        if (defField->getEsdlType()==EsdlTypeEnumRef)
-            return outputEnumString(size, text, fieldname, defField);
+        const char *defTypeName = NULL;
+        bool isArray = (parent->defObj && parent->defObj->getEsdlType()==EsdlTypeArray);
+        if (isArray)
+            defTypeName = parent->defObj->queryProp("type");
+        else
+        {
+            IEsdlDefStruct *defStruct = queryCurrentEsdlStruct();
+            if (!defStruct)
+                return;
+            IEsdlDefObject *defField = defStruct->queryChild(fieldname);
+            if (!defField)
+                return;
+            if (defField->getEsdlType()==EsdlTypeEnumRef)
+                return outputEnumString(size, text, fieldname, defField);
+            defTypeName = defField->queryProp("type");
+        }
 
-        const char *defType = defField->queryProp("type");
-        if (!defType)
+        if (!defTypeName)
             return;
-        const char *javaSig = esdl2JavaSig(*esdl, defType);
-        jfieldID fieldId = JNIenv->GetFieldID(getCurJavaClass(), fieldname, javaSig); //tbd cache this
-        if (!fieldId)
+        const char *javaSig = esdl2JavaSig(*esdl, defTypeName);
+        if (!javaSig)
             return;
 
-        const char *fieldClassName = esdl2JavaFullClassName(*esdl, defType);
+        const char *fieldClassName = esdl2JavaFullClassName(*esdl, defTypeName);
         jclass typeClass = FindClass(fieldClassName);
         jmethodID typeStringConstructor = JNIenv->GetMethodID(typeClass, "<init>", "(Ljava/lang/String;)V"); //All types currently used for ESDL mapping have string constructors
         StringAttr s(text, size);
@@ -2115,7 +2175,16 @@ public:
         jobject value = JNIenv->NewObject(typeClass, typeStringConstructor, strvalue);
         JNIenv->DeleteLocalRef(strvalue);
         checkException();
-        JNIenv->SetObjectField(getCurJavaObject(), fieldId, value);
+        if (!value)
+            return;
+        if (isArray)
+            JNIenv->CallObjectMethod(parent->obj, parent->append, value);
+        else
+        {
+            jfieldID fieldId = JNIenv->GetFieldID(getCurJavaClass(), fieldname, javaSig);
+            if (fieldId)
+                JNIenv->SetObjectField(getCurJavaObject(), fieldId, value);
+        }
         JNIenv->DeleteLocalRef(value);
         checkException();
     }
@@ -2208,12 +2277,14 @@ public:
                 const char *structType = child->queryProp("type");
                 if (structType)
                     return esdl->queryObj(structType);
+                break;
             }
             case EsdlTypeElement:
             {
                 const char *structType = child->queryProp("complex_type");
                 if (structType)
                     return esdl->queryObj(structType);
+                break;
             }
             default:
                 break;
@@ -2252,8 +2323,11 @@ public:
     }
     virtual void outputEndNested(const char *fieldname)
     {
-        if (defStack.length()>1 && streq(fieldname, defStack.tos().name)) //don't destroy root object yet
-            popDefStackEntry(JNIenv);
+        if (defStack.length()<=1)  //don't destroy root object yet
+            return;
+        if (!streq(fieldname, defStack.tos().name)) //should be exception? or forgive and forget?
+            return;
+        popDefStackEntry(JNIenv);
     }
     virtual void outputSetAll()
     {
@@ -2273,14 +2347,14 @@ public:
 
 public:
     JNIEnv *JNIenv;
+    Linked<IEsdlDefinition> esdl;
     StringAttr javaPackage;
     StringAttr esdlType;
-    Linked<IEsdlDefinition> esdl;
     class DefStackEntry : public CInterface, implements IInterface
     {
     public:
         IMPLEMENT_IINTERFACE;
-        DefStackEntry(const char *fieldname, IEsdlDefObject *_defType, IEsdlDefObject *_defObj) : name(fieldname), defType(_defType), defObj(_defObj)
+        DefStackEntry(const char *fieldname, IEsdlDefObject *_defType, IEsdlDefObject *_defObj) : name(fieldname), defType(_defType), defObj(_defObj), Class(0), obj(0), constructor(0), append(0), fieldId(0)
         {
         }
         ~DefStackEntry()
@@ -2298,21 +2372,27 @@ public:
         jobject obj;
     };
 
+    jobject MakeObjectGlobal(jobject local)
+    {
+        if (!local)
+            return 0;
+        jobject global = JNIenv->NewGlobalRef(local);
+        JNIenv->DeleteLocalRef(local);
+        return global;
+    }
     jclass FindClass(const char *name)
     {
         jclass *pClass = javaClasses.getValue(name);
         if (pClass)
             return *pClass;
-        jclass localClass = JNIenv->FindClass(name);
-        if (!localClass)
-            return 0;
-        jclass Class = (jclass) JNIenv->NewGlobalRef(localClass);
-        javaClasses.setValue(name, Class);
-        JNIenv->DeleteLocalRef(localClass);
+        jclass Class = (jclass) MakeObjectGlobal(JNIenv->FindClass(name));
+        javaClasses.setValue(name, Class); //even save if result has no class
         return Class;
     }
     void popDefStackEntry(JNIEnv *JNIenv)
     {
+        if (!defStack.length())
+            return;
         Owned<DefStackEntry> entry = &defStack.popGet();
         if (entry->obj)
             JNIenv->DeleteGlobalRef(entry->obj);
@@ -2330,11 +2410,10 @@ public:
             {
                 entry->constructor = JNIenv->GetMethodID(entry->Class, "<init>", "()V");
                 entry->append = JNIenv->GetMethodID(entry->Class, "add", "(Ljava/lang/Object;)Z");
-                jobject localObj = JNIenv->NewObject(entry->Class, entry->constructor);
+                entry->obj = MakeObjectGlobal(JNIenv->NewObject(entry->Class, entry->constructor));
                 javaembed::checkException(JNIenv, false);
-                if (localObj)
+                if (entry->obj)
                 {
-                    entry->obj = JNIenv->NewGlobalRef(localObj);
                     if (parent && parent->Class)
                     {
                         VStringBuffer javaSig("L%s;", javaClassName);
@@ -2352,17 +2431,14 @@ public:
             if (entry->Class)
             {
                 entry->constructor = JNIenv->GetMethodID(entry->Class, "<init>", "()V");
-                jobject localObj = JNIenv->NewObject(entry->Class, entry->constructor);
+                entry->obj = MakeObjectGlobal(JNIenv->NewObject(entry->Class, entry->constructor));
                 javaembed::checkException(JNIenv, false);
-                if (localObj)
+                if (entry->obj)
                 {
-                    entry->obj = JNIenv->NewGlobalRef(localObj);
                     if (parent)
                     {
                         if (parent->defObj && parent->defObj->getEsdlType()==EsdlTypeArray)
-                        {
                             JNIenv->CallObjectMethod(parent->obj, parent->append, entry->obj);
-                        }
                         else if (parent->Class)
                         {
                             VStringBuffer javaSig("L%s;", javaClassName.str());