cancel
Showing results for 
Search instead for 
Did you mean: 

extract data from hash map for target structure mapping

Former Member
0 Kudos

Hello All

i have written a udf to extract material id and its related data in a hash map

hash map looks like below {Material_ID=[plant,uom,salesorg] combination - there are many material ids in the hashmap as shown below

{0061399=[1592345, 26728282, 29798989], 006139940=[1596889, 292909, 2989898], 006139996=[15965677, 989889, 990090] ...........................}

I want to extract these values inside the square braces and map it to the corresponding PI message mapping target structure fields for plant, uom, sales org. How do I achieve this?

The source structure is something like below

<Material>1.unbounded

<Material_ID/>

<plant/>

<uom/>

<salesorg/>

</Material>

---

---

---

Target structure is like below

<Mat>1.unbounded

<Mat_ID/>

<werks/>

<unitofMeas/>

<salesorganiz/>

</Mat>

---

thx

mike

Accepted Solutions (0)

Answers (1)

Answers (1)

iaki_vila
Active Contributor
0 Kudos

Hi Michael,

I think i dont understand right your problem, if you have yet the UDF developed you only need to set plant,uom,salesorg as UDF input parameter and the output to Mat_ID, the context youn can let it by the defect option and you only need to map Material to Mat in order to keep the right context.

Regards.

Former Member
0 Kudos

thanks. Might not have explained clearly..sorry about that.

i have written in NWDS java project using a sample XML as input for testing and not yet imported the code into PI UDF. Thats where the confusion is mainly. how and what to tweak while importing to PI message mapping UDF level.

The nwds method would look like this below

"public static Map<String, ArrayList<String>> findTupleIds() throws ParserConfigurationException, SAXException, IOException {"

Map <String, ArrayList<String>> multimap= new HashMap<String, ArrayList<String>>();

And finally return multimap;

I dont know how to bring this into PI udf level.

How do I write a udf to return a hashmap in PI. what would be the declaration of udf? Where should I write udf? is it in the init section or somewhere else? Do I need to use all values of a queue in this case? because I am taking all material Ids and corresponding sales org as input parameters and returning the output in hashmap after doing some business logic.

so now question is clear I believe. Let me know if not ...thx

thx

mike

Former Member
0 Kudos

Hi Michael

Why don't you write a java mapping itself in NWDS which can read the values from hash table and then populate the target structure based on the values.

You will get many blogs on java mapping in SCN..

Former Member
0 Kudos

Hi Indrajit,

I didn't see anywhere on hashmap for SAP java mapping in scn ...can you please send me over some links where they have used hashmap for similar cases...

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

If you scroll down near the bottom to one of my posts on this subject there is an example of using a hashmap in a Function Library:

The post I am referencing is from June 1st.

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan,

you are a great java programmer as it looks from the post. My questions are below

a) when I am using hashmap for storing material id as key and values are uom, plant & sales org

how do I use that for mapping to the respective target structure fields? We can pass return parameters as resultlist but not hashmap in udf. I am wondering how to extract these values from hashmap in udf? for printing in the screen is fine but how can we extract from hashmap?

"because I am taking all material Ids and corresponding sales org as input parameters and returning the output in hashmap after doing some business logic"


so the final hashmap looks like


"public static Map<String, ArrayList<String>> findTupleIds() throws ParserConfigurationException, SAXException, IOException {"

Map <String, ArrayList<String>> multimap= new HashMap<String, ArrayList<String>>();

And finally return multimap;


Result:

{Material_ID=[plant,uom,salesorg]


{0061399=[1592345, 26728282, 29798989], 006139940=[1596889, 292909, 2989898], 006139996=[15965677, 989889, 990090] ...........................}


so am trying to avoid java mapping here and instead planning to use udf


Is there a way we can pass multiple output parameters in the same udf instead of a single output parameter such as resultlist or string? am kind of really


thx

mike




Former Member
0 Kudos

am kind of really puzzled now..:)

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

Can you send me a smaller sample of your source XML and then what you would be expected as your target XML?  If I see that then it will give me a better idea of what a good way to accomplish your requirement would be.  I saw this string: {0061399=[1592345, 26728282, 29798989], 006139940=[1596889, 292909, 2989898], 006139996=[15965677, 989889, 990090] ...........................}

but I wasn't sure how this played a role in your message mapping.

Regards,

Ryan Crosby

Former Member
0 Kudos

Sure Ryan

Source XML structure

<MATERIALS>

   <MATERIAL>

      <MATERIAL>123</FTX_MATERIAL>

        <SALESORG>

          <OrgComplete>Y</OrgComplete>

           <VAL_SALESORG>

                 <DisplayValue>A12</DisplayValue>

          </VAL_SALESORG>

         </SALESORG>

        <SALESORG>

          <OrgComplete>Y</OrgComplete>

           <VAL_SALESORG>

                 <DisplayValue>B12</DisplayValue>

          </VAL_SALESORG>

         </SALESORG>

    </MATERIAL>

   <MATERIAL>

      <MATERIAL>456</FTX_MATERIAL>

        <SALESORG>

          <OrgComplete>Y</OrgComplete>

           <VAL_SALESORG>

                 <DisplayValue>C12</DisplayValue>

          </VAL_SALESORG>

         </SALESORG>

      <SALESORG>

          <OrgComplete>Y</OrgComplete>

           <VAL_SALESORG>

                 <DisplayValue>D12</DisplayValue>

          </VAL_SALESORG>

         </SALESORG>

    </MATERIAL>

</MATERIALS>

I use all materials and do a soap lookup from message mapping to another system to retrieve the below response structure which I called as intermediate XML structure 

Intermediate XML structure

<Material_Resp> 1..1

  <Material>-----------> 1..unbounded

          <Mat_ID> 123 </Mat_ID>

   <Sales_Org> -------->1..unbounded

          <SalesOrg_ID> A12 <SalesOrg_ID>

          <Orgcomplete>Y</Orgcomplete>

          <Tuple>

              <TupleVal>1456</TupleVal>

           </Tuple>

    </Sales_Org

    <Sales_Org>

           <SalesOrg_ID> B12 <SalesOrg_ID>

           <Orgcomplete>N</Orgcomplete>

           <Tuple>

              <TupleVal></TupleVal>

           </Tuple>

    </Sales_Org

   <Materiall_Ident> ----------> 1....1

       <Value>Phar</Value>

   <Material_Ident>

  </Material>

  <Material>

          <Mat_ID> 456 </Mat_ID>

   <Sales_Org>

          <SalesOrg_ID> C12 <SalesOrg_ID>

          <Orgcomplete>Y</Orgcomplete>

          <Tuple>

              <TupleVal>1408</TupleVal>

           </Tuple>

    </Sales_Org

    <Sales_Org>

           <SalesOrg_ID> D12 <SalesOrg_ID>

           <Orgcomplete>N</Orgcomplete>

           <Tuple>

              <TupleVal></TupleVal>

           </Tuple>

    </Sales_Org

   <Materiall_Ident>

       <Value>Exe</Value>

   <Material_Ident>

  </Material>

</Material_Resp>

Final Target XML structure

<MaterialTargResp>

     <Material>  -----------> 1..unbounded

          <Mat_ID> 123 </Mat_ID>

    <Materiall_Ident> ----------> 1....1

       <Value>Phar</Value>

    <Material_Ident>

    <Sales_Org> -------->1..unbounded

         <Orgcomplete>Y</Orgcomplete>

          <Tuple>

                <TupleVal>1456</TupleVal>

           </Tuple>

     </Sales_Org>

     </Material>

   <Material>  -----------> 1..unbounded

          <Mat_ID> 456 </Mat_ID>

    <Materiall_Ident> ----------> 1....1

       <Value>Exe</Value>

    <Material_Ident>

    <Sales_Org> -------->1..unbounded

         <Orgcomplete>Y</Orgcomplete>

          <Tuple>

                <TupleVal>1408</TupleVal>

           </Tuple>

     </Sales_Org>

     </Material>

</MaterialTargResp>

Business logic:

Take the  <SalesOrg_ID> from source structure (source XML) whose values are matching with the soap response lookup (Intermediate structure) sales org ID values and if matches, pass the corresponding tuple values to the final target structure provided tuple values are not empty in the soap response.

Along with this, pass other values from source XML such as Mat_ID, Material_Ident, OrgComplete.

In the example shown above, I am getting tuple values only for 2 sales orgs and other two are empty.

hope this is clear

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

So you only need to take the intermediate message type of Material_Resp and map it to MaterialTargResp or you need to take message type MATERIALS and parse data from Material_Resp and associate that information together to get MaterialTargResp?  If you are doing the former then I don't think you need any special UDF logic to handle that.

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan

I am not using the former. I am using only one message mapping and a soap lookup. So the latter part is what I'm doing

" you need to take message type MATERIALS and parse data from Material_Resp and associate that information together to get MaterialTargResp? "


thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

I have never had the need to use soap lookup before in a mapping.  Is that a straight XML stream that would need to be parsed in the code to make the associations for the keys and values?

Regards,

Ryan Crosby

Former Member
0 Kudos

Thanks Ryan for the response. I have taken care of the soap lookup call to get the response as intermediate XML. that is straight XML stream with no difference from XML.

I am using this XML as the source for writing the parser and thereby collecting in hashmap. This is where I need your help and also to associate the source XML and this hashmap to map to final target structure

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

Can you show me the code of how you parsed the XML and how you constructed the hash map.  If I have some understanding of how that has been organized I think I can help you get to where you need to be.  Right now I'm a little hazy as to what your hashmap is like... is it constructed in this fashion from your example:

0061399 maps to [1592345, 26728282, 29798989]  ??

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan.

Sorry for the delayed reply because I was workin on the java function attached for you to modify if you may. And thanks again for your response. The program is attached which I am using to parse the XML and collect material id and its tuple values. If I execute the function in a main method, I will get the result as

material is 123  ->debugging purpose

mattuple {123=[1456]}  - whatever matching with A12. But the value need not be A12 always. It can be any other value taken from the source XML VAL_SALESORG displayvalue element. for simplicity, I have hardcoded in the function.

which means {MatID1=[tupleval1], MatID2=[tupleval2]......}

I am sending you the intermediate XML structure again which is what I am parsing using the attached java parser function. Please ignore the previous intermediate XML structure. Source XML structure hasn't changed..you can refer to that. And also target XML remains the same

Intermediate XML structure

<Material_Resp> 1..1

  <Material>-----------> 1..unbounded

          <Mat_ID> 123 </Mat_ID>

   <Sales_Org> -------->1..unbounded

          <SalesOrg_ID>

            <displayValue>A12</displayValue>

          <SalesOrg_ID>

          <Orgcomplete>Y</Orgcomplete>

          <Tuple>

              <TupleVal>1456</TupleVal>

           </Tuple>

    </Sales_Org

    <Sales_Org>

           <SalesOrg_ID>

              <displayValue>B12</displayValue>

          </SalesOrg_ID>

           <Orgcomplete>N</Orgcomplete>

           <Tuple>

              <TupleVal></TupleVal>

           </Tuple>

    </Sales_Org

   <Materiall_Ident> ----------> 1....1

       <Value>Phar</Value>

   <Material_Ident>

  </Material>

  <Material>

          <Mat_ID> 456 </Mat_ID>

   <Sales_Org>

          <SalesOrg_ID>

           <displayValue>C12</displayValue>

         </SalesOrg_ID>

          <Orgcomplete>Y</Orgcomplete>

          <Tuple>

              <TupleVal>1408</TupleVal>

           </Tuple>

    </Sales_Org

    <Sales_Org>

           <SalesOrg_ID>

            <displayValue>D12</displayValue>

         </SalesOrg_ID>

           <Orgcomplete>N</Orgcomplete>

           <Tuple>

              <TupleVal></TupleVal>

           </Tuple>

    </Sales_Org

   <Materiall_Ident>

       <Value>Exe</Value>

   <Material_Ident>

  </Material>

</Material_Resp>

thanks a lot,

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi,

Ok, I can see you are building a HashMap ok but it does appear that you are trying it with a file as some sort of test?  So now you want to figure out how to get this into your mapping as a UDF to parse the response from the soapLookup?  If so, can you show me the mapping piece where you execute your soapLookup?

Regards,

Ryan Crosby

Former Member
0 Kudos

Well Ryan, that's the place I am not sure how to bring it to mapping.

As I said, I am using source XML as the source structure in message mapping and final target XML structure as the target structure. There is no intermediate structure in my mapping because that is inside the soap lookup which I have build. The soap lookup creates webservice request using concatenate function with material ids as one of the fields in the webservice request and gets the soap response as Intermediate XML structure format with data. And that's the XML file with soap response data for simulating the real webservice call during runtime that I am using in the JAVA function which you were saying some sort of file.

I dont know where to place the soap lookup which will have the attached java function program after the webservice response in the same UDF. And also how to map the target fields in the target XML structure from hashmap values collected after the soap call and parsing using DOM and hashmap.

/Here is the soap lookup code for your understanding.

try {

//instance the channel to invoke the service.

Channel channel = LookupService.getChannel("BS_SOAP", "CC_SOAPLookup");

SystemAccessor accessor = LookupService.getSystemAccessor(channel);

// The Request message in XML. THIS IS THE LOOKUP SERVICE

Header = "<MATERIAL xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:Search_MaterialWS\" xmlns:urn1=\"urn:com.sap.soap.ws.beans.mat_search_in\" xmlns:urn2=\"urn:com.sap.soap.ws.beans\" xmlns:urn3=\"urn:com.sap.soap.core.beans\">";

  Record = "";

  //Record = Record + " <MATERIAL>";

  Record = Record + " <urn:query>";

  Record = Record + "<urn1:criteria>";

  Record = Record + " <urn1:MATERIAL>";

      Record = Record + " <urn2:logicalOperator>OR</urn2:logicalOperator>";

//looping thru each material ID and pass it to the webservice request structure

  for (int matcount= 0; matcount<matid.length; matcount++) {

  Record = Record + "<urn2:constraint>";

  Record = Record + "<urn2:value>" + matid[matcount] + "</urn2:value>"; 

    Record = Record + "<urn2:expressionOperator>" + "equals" + "</urn2:expressionOperator>";

    Record = Record + "</urn2:constraint>";

}

       Record = Record + "</urn1:MATERIAL>";

    Record = Record + "</urn1:criteria>";

   Record = Record +  " <urn1:resultDefinition>";

Record = Record + "<urn2:fieldDefinition>";

Record = Record + "<urn2:fieldCode>SalesOrg/urn2:fieldCode>";

  Record = Record + "</urn2:fieldDefinition>";

     Record = Record + "<urn2:fieldDefinition>";

      Record = Record + "<urn2:fieldCode>MATERIAL</urn2:fieldCode>";

    

  SoapReq = Header + Record + " <MATERIAL>";

trace.addWarning(SoapReq);

result.addValue(SoapReq);

InputStream inputStream =new ByteArrayInputStream(SoapReq.getBytes());

XmlPayload payload = LookupService.getXmlPayload(inputStream);

Payload SOAPOutPayload = null;

//The response will be a Payload. Parse this to get the response field out.

SOAPOutPayload = accessor.call(payload);

/* Parse the SOAPPayload to get the SOAP Response back.  */

InputStream inp = SOAPOutPayload.getContent(); 

Next line of code would be the java function to parse the webservice response

All these are in the same UDF because then I can pass all material IDs in one shot from the source XML structure to this webservice request and get the response for all the material IDs and parse using the DOM parser and hashmap. And collect corresponding values in hashmap for each material Id and use it for target structure XML mapping. This is where I need your help Ryan for guiding through how to achieve this.

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

Are you doing a graphical mapping for the soapLookup?  If so, can you show me a screenshot of where you have placed that?  I think the idea would be to setup the mapping of the source and target messages like this:

for the soapLookup function you would need something like this that represents the XML string at the end of the UDF:


result.addValue(xmlStr);

for the buildHashMap function the basic structure of it would look like this maybe:


HashMap matMap<String, ArrayList<String>> = new HashMap<String, ArrayList<String>>();

// Put your XML parsing code here with reference to val1[0]

container.getGlobalContainer();

container.setParameter("matmap", matMap);

result.addValue("");

So in the soapLookup function you would add one full XML string to the result list that would then get parsed in the next UDF for building the hash map.  You instantiate the HashMap at the start and then build it with that other code you had.  Then at the end you would call the GlobalContainer to store the map with some name so you can get it back.  The last bit for adding a result value is so that the target message root node would get created based on the results of the buildHashMap UDF.  For something like this since you only want it to execute once you could do "All Values of Queue" for the execution type.  Then for the fields where you need to read the HashMap tuple values you can call the following code:


HashMap matMap<String, ArrayList<String>> = (HashMap<String, ArrayList<String>>) container.getGlobalContainer.getParameter("matmap");

Hope this helps you fit it together...

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan

Thanks a lot for your response

I am attaching screenshot of message mapping which has the screenshot of message mapping I am working on as per your recommendation

There is a slight change in requirement to get the material identification value also from the response structure of webservice. So I have to collect those values also in hashmap along with tuple values

And then I am not sure if the getparameter("matmap") will fetch tuple and material internal id in the way you mentioned? how does it fetch the correct tuple and material internal ID values as per the right context of materail ID? I am not getting it properly

thx

mike

Former Member
0 Kudos

Ryan,

The target structure I made a mistake. All elements Material_Ident, Sales Org come under material 1..unbounded element

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

First lets work on your requirement for the tuples.  For the additional field that will get more complicated and you'll need to setup an imported archive with a Java class to hold all of your data related to materialID because a hashmap is a single key to a single object.  So to read the map and get data for materialID you would have something like the following in a UDF:


HashMap<String, ArrayList<String>> matMap = (HashMap<String, ArrayList<String>>) container.getGlobalContainer().getParameter("matmap");

for(int i = 0; i < var1.length; i++)

{

ArrayList<String> al = matMap.get(var1[i]);

matMap.remove(var1[i]);

for(int j = 0; j < al.size(); j++)

{

result.addValue(al.get(j));

}

}

Line 05 isn't important but you could start removing data if you've already retrieved it.  If you have duplicates in your output you would have to remove line 05 though.  So you would take the materialID values as input to a UDF like this and then add result values and then you simply need to manage your contexts using the node functions or you can add some logic here.

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan

I was building the function hashmap and it gave me an error for the last function step below

The function is not able to get the global container parameter matmap it seems

"

  1. HashMap<String, ArrayList<String>> matMap = (HashMap<String, ArrayList<String>>) container.getGlobalContainer().getParameter("matmap"); 
  2. for(int i = 0; i < var1.length; i++) 
  3. ArrayList<String> al = matMap.get(var1[i]); 
  4. matMap.remove(var1[i]); 
  5. for(int j = 0; j < al.size(); j++) 
  6. result.addValue(al.get(j)); 

        Exception:[java.lang.NullPointerException: while trying to invoke the method java.util.HashMap.get(java.lang.Object) of a null object loaded from a local variable at slot 4] in class com.sap.xi.tf._MM_hashmap_ method ExtractTupleFromMAT[[Ljava.lang.String;@373ed4ce, com.sap.aii.mappingtool.tf7.rt.ResultListImpl@7e053cb7, com.sap.aii.mappingtool.tf7.rt.Context@9d55304]

See error logs for details    

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

Can you show me the source code where you are building the HashMap?  The String key is case-sensitive I believe so you have to be careful with that.  As long as you are building that at the root level of the source -> target message root node then I think that will execute first and you should be able to retrieve your HashMap from the GlobalContainer object.  Go ahead and include the source code for the UDF where you construct the HashMap and I'll check it for you.

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan,

thanks a lot for your reply.

i wrote one UDF for building the hashmap which has the following 2 lines of code to set parameter

GlobalContainer globalContainer = container.getGlobalContainer();

globalContainer.setParameter("mattuple",matTuple);

result.addValue(""); 

then the next udf to extract hashmap for each tuple is written as below

public void ExtractTuple(String[] matid, ResultList result, Container container) throws StreamTransformationException{

GlobalContainer globalContainer1 = container.getGlobalContainer();

HashMap<String, ArrayList<String>> matTuple = (HashMap<String, ArrayList<String>>)globalContainer1.getParameter("mattuple"); 

AbstractTrace trace = container.getTrace();

for(int i = 0; i <matid.length; i++) 

ArrayList<String> al = matTuple.get(matid[i]); 

matTuple.remove(matid[i]); 

for(int j = 0; j < al.size(); j++) 

result.addValue(al.get(j)); 

this giving the exception mentioned before

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

If you have called the constructor for the HashMap and stored it in the GlobalContainer it should be working ok.  One thing that could be an issue is the order of how things execute.  I would suggest adding the trace messages to each UDF so you can see how it's executing.  Something like this:

MappingTrace trace = container.getTrace();

trace.addInfo("building map");

then in the extract UDF something similar where you have a message like "Extracting tuples".  If the map isn't constructed in memory before you try to read from it then it would explain the NullPointerException.

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan,

Trace is executed in order building map first and then extracting tuples.

Still getting the same error when I display queue in extracting tuples

        Exception:[java.lang.NullPointerException: while trying to invoke the method java.util.HashMap.get(java.lang.Object) of a null object loaded from a local variable at slot 5] in class com.sap.xi.tf._MM_MatSearchhashmap_ method ExtractTuple[[Ljava.lang.String;@57970a27, com.sap.aii.mappingtool.tf7.rt.ResultListImpl@7d1bb35d, com.sap.aii.mappingtool.tf7.rt.Context@259cded6]

See error logs for details    

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

I noticed that you have mentioned two different names for the getParameter so I want to see which one you are currently using.  The first time you mentioned this way:


HashMap<String, ArrayList<String>> matMap =

(HashMap<String, ArrayList<String>>) container.getGlobalContainer().getParameter("matmap");

And the second time you mentioned this code:


GlobalContainer globalContainer1 = container.getGlobalContainer();

HashMap<String, ArrayList<String>> matTuple =

(HashMap<String, ArrayList<String>>)globalContainer1.getParameter("mattuple");

If you set the HashMap in the GlobalContainer this way:


GlobalContainer globalContainer = container.getGlobalContainer();

globalContainer.setParameter("mattuple",    matTuple);

That would mean that you could only use the second piece of code to get the map and end up with a non-null object.  I believe the GlobalContainer based on how it operates is similar to a HashMap and has to be referenced by the exact name with which you store the parameter originally to be able to get it back.  Also, can you show the whole UDF for the place where you are building the map?

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan

I tried all I could but global container paramter get is still not finding the parameter name. throwing null exception while getting the parameter. I tried this by creating a test mapping with a dummy source and target by passing string value instead of hashmap and that is working while gettiinng the string back using getparamater. But hashmap is not working there too. there is some issue with hashmap

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

I know this is possible because the setParameter() method for the GlobalContainer takes an Object type for the second parameter meaning that you can put any Java Object as the value.  I have actually done this same sort of thing with the GlobalContainer for one of my requirements to read email addresses of employees on some HR interfaces.  The only difference is that I did it using a Function Library similar to the post I highlighted before.  Can you share the local UDF code where you have constructed and inserted the HashMap into the GlobalContainer?

Regards,

Ryan Crosby

Former Member
0 Kudos

Hi Ryan

Thanks for the reply

here is the code

public String setglblcontainer(String acktext, Container container) throws StreamTransformationException{

GlobalContainer globalContainer1 = container.getGlobalContainer();

AbstractTrace trace = container.getTrace();

HashMap<String, ArrayList<String>> matTuple =  new HashMap<String, ArrayList<String>>();

ArrayList<String> valSetOne = new ArrayList<String>();

valSetOne.add("Apple");

  matTuple.put("A", valSetOne);

    trace.addInfo("executed get" + valSetOne[0]);

    globalContainer1.setParameter("matmap", matTuple);

return"";

HashMap<String, ArrayList<String>> matmap1 = (HashMap<String, ArrayList<String>>) container.getGlobalContainer().getParameter("matmap");

AbstractTrace trace = container.getTrace();

trace.addInfo("getmethod");

for(int i = 0; i<coreid.length; i++) 

ArrayList<String> al = matmap1.get(coreid[i]); 

matmap1.remove(coreid[i]); 

for(int j = 0; j<al.size(); j++) 

result.addValue(al.get(j)); 

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

Ok, the only possible place I can see for a NullPointerException is the for loop with this code based on what you have included:


ArrayList<String> al = matmap1.get(coreid[i]); 

matmap1.remove(coreid[i]); 

for(int j = 0; j<al.size(); j++) 

result.addValue(al.get(j)); 

It looks like you have hard-coded a test key of "A" with an ArrayList of one value.  If you test with a payload that includes any material that is not equal to "A" then you will get a NullPointerExeption in the for loop processing.  The reason why is that the HashMap will pass back a null value for ArrayList<String> al when the key doesn't exist so you cannot call al.size() on your check condition for a null value.  Can you confirm that you are only testing this with a payload materialID of "A" in your sample?

Regards,

Ryan Crosby

Former Member
0 Kudos

yes Ryan. thats right. I am passing coreid as A and executing in test message mapping

and when I display the queue for the public getglblcontainer(String[] coreid, ResultList result, Container container) throws StreamTransformationException function, I get the following error message

        Exception:[java.lang.NullPointerException: while trying to invoke the method java.util.HashMap.get(java.lang.Object) of a null object loaded from a local variable at slot 4] in class com.sap.xi.tf._MM_Testglobalcontainer_ method getglblcontainer[[Ljava.lang.String;@2374a1b5, com.sap.aii.mappingtool.tf7.rt.ResultListImpl@3e248442, com.sap.aii.mappingtool.tf7.rt.Context@4f6728ed]

See error logs for details    

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

I just tried a sample of your code and I got a compiler error for the UDF that builds the HashMap:


GlobalContainer globalContainer1 = container.getGlobalContainer();

AbstractTrace trace = container.getTrace();

HashMap<String, ArrayList<String>> matTuple =  new HashMap<String, ArrayList<String>>();

ArrayList<String> valSetOne = new ArrayList<String>();

valSetOne.add("Apple");

  matTuple.put("A", valSetOne);

    trace.addInfo("executed get" + valSetOne[0]);

    globalContainer1.setParameter("matmap", matTuple);

return"";

The code in line 09 is invalid because you cannot reference a String value in an ArrayList<String> like you can with a regular array... you have to use valSetOne.get(0) instead.  Also instead of having return ""; in the last line I would change that for result.addValue(""); I used that code and it works. The last thing I did was remove the setup of the trace object in line 02 since it should be working with this below.  Here is the buildMap code I used that worked in my system:


GlobalContainer globalContainer1 = container.getGlobalContainer();

HashMap<String, ArrayList<String>> matTuple =  new HashMap<String, ArrayList<String>>();

ArrayList<String> valSetOne = new ArrayList<String>();

valSetOne.add("Apple");

matTuple.put("A", valSetOne);

globalContainer1.setParameter("matmap", matTuple);

result.addValue("");

Regards,

Ryan Crosby

Former Member
0 Kudos

Thanks Ryan again

I used the code above and I got the exact same error again. It looks like there is some issue with setting and getting parameters using global container for hashmap. can you please share the mapping and two functions you used. I will try to do exactly what you did even though I believe we both are doing the same. I am on PI 7.4 single stack and trying in Swing of ESR and not in NWDS. Which version are you trying?

thx

mike

Former Member
0 Kudos

PO 7.4 single stack to be precise

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

I have tested this on both our PI 7.11 system and our new PO 7.4 single stack and it works on both.  Here are the basic screenshots of my map and the code for buildMap I posted above and the code for getting tuples I used your code.

Source & Target structures (I simplified for the test):

Message level:

'

Material:

Material ID:

Tuple:

Regards,

Ryan Crosby

Message was edited by: Ryan Crosby

Former Member
0 Kudos

thanks Ryan. In the trace, I can see the array list is having the value Apple

and also result too is having the value Apple.

but it's not finally sending to the target field.

display queue for the getglobalcontainer is still throwing the same error

        Exception:[java.lang.NullPointerException: while trying to invoke the method java.util.HashMap.get(java.lang.Object) of a null object loaded from a local variable at slot 4] in class com.sap.xi.tf._MM_Testglobalcontainer_ method getglblcontainer[[Ljava.lang.String;@75ae0b23, com.sap.aii.mappingtool.tf7.rt.ResultListImpl@755d6330, com.sap.aii.mappingtool.tf7.rt.Context@461b2082]

See error logs for details    

Is it trying to say array list a1 below while executing the statement

al = matmap1.get(coreid[i]);  is getting null? I think so. But when I added trace to display

using //trace.addInfo("arraylist" + al); I get the value for a1 as Apple in the trace

it seems there is some bug in the system right? how would you advise to move forward? is there any other way other than hashmap to deal with it? As I said before, this occurs for hashmap but not for String and all...please advise..am stuck now...

HashMap<String, ArrayList<String>> matmap1 = (HashMap<String, ArrayList<String>>) container.getGlobalContainer().getParameter("matmap");

ArrayList<String> al = new ArrayList<String>();

//AbstractTrace trace = container.getTrace();

//trace.addInfo("getmethod");

for(int i = 0; i<coreid.length; i++) 

al = matmap1.get(coreid[i]); 

//trace.addInfo("coreid is" + coreid[i]);

//trace.addInfo("arraylist" + al);

//matmap1.remove(coreid[i]); 

for(int j = 0; j<al.size(); j++) 

result.addValue(al.get(j)); 

//trace.addInfo("result" + al.get(j));

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

For this code you have:


HashMap<String, ArrayList<String>> matmap1 = (HashMap<String, ArrayList<String>>) container.getGlobalContainer().getParameter("matmap");

ArrayList<String> al = new ArrayList<String>();

//AbstractTrace trace = container.getTrace();

//trace.addInfo("getmethod");

for(int i = 0; i<coreid.length; i++) 

al = matmap1.get(coreid[i]); 

//trace.addInfo("coreid is" + coreid[i]);

//trace.addInfo("arraylist" + al);

//matmap1.remove(coreid[i]); 

for(int j = 0; j<al.size(); j++) 

result.addValue(al.get(j)); 

//trace.addInfo("result" + al.get(j));

The error you are getting is happening at line 07.  However, that exception is occurring because of the assignment of a null object to matmap1 at line 01.  So the retrieval of the parameter "matmap" from the GlobalContainer is bringing back a null value that gets assigned to matmap1.  Since the object is null then you cannot invoke the method get() because there is no such method for a null object.  I have used this same code in my system so I think you would need to use the xpi inspector and possibly search OSS for issues regarding the mapping runtime.

Regards,

Ryan Crosby

Former Member
0 Kudos

sure Ryan. Would it help to use anything other than hashmap to resolve this for the timebeing?

As I said before, there are two values per key in my case. So eventually either I have to use 2 hashmaps for 2 fields in the target or do in one shot both like you mentioned class and import as archive and refer that in mapping. Could you help me with that thing if it is not too difficult?

The project is going on tight schedule and I was told to resolve this with some other way if possible..thats why...

One value as you know is tuples (which is array list)

And other value is material internal ID

so it would be like this

Key is Material_ID

Values are tuples(ArrayList) & Material Internal Id (String)

thx

mike

Former Member
0 Kudos

Ryan

To clarify again, Both tuples and material internal ID are coming in the soap response structure which I am parsing using DOM and storing in hashmap now only for tuples.

I have written a separate function for the timebeing to parse the response structure and read material internal id per material and store in a hashmap again like

{Material_Id=[material_internal_ID], .....}

So we have to put these two functions in a single function and store the values in a class for the key material Id as per your suggestion before...

thx

mike

Former Member
0 Kudos

Again, if you see the final target response structure, the material internal Id goes to

<Materiall_Ident/Value





<MaterialTargResp>

     <Material>  -----------> 1..unbounded

          <Mat_ID> 123 </Mat_ID>

    <Materiall_Ident> ----------> 1....1

       <Value>Phar</Value>

    <Material_Ident>

    <Sales_Org> -------->1..unbounded

         <Orgcomplete>Y</Orgcomplete>

          <Tuple>

                <TupleVal>1456</TupleVal>

           </Tuple>

     </Sales_Org>

     </Material>

   <Material>  -----------> 1..unbounded

          <Mat_ID> 456 </Mat_ID>

    <Materiall_Ident> ----------> 1....1

       <Value>Exe</Value>

    <Material_Ident>

    <Sales_Org> -------->1..unbounded

         <Orgcomplete>Y</Orgcomplete>

          <Tuple>

                <TupleVal>1408</TupleVal>

           </Tuple>

     </Sales_Org>

     </Material>

</MaterialTargResp>

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

I don't think the issue has to do with the use of HashMap but rather the retrieval of parameters from the GlobalContainer.  One thing you could try is by setting these UDFs up as a Function Library instead of local UDFs in the mapping and create your HashMap in the init function.  Then add two additional functions to the library for putting values and retrieving values.  You could also use a HashSet for getting your two types of values but that would require generating the imported archive for a class that would implement the Comparable interface for Java.  Not sure that is worth the effort currently though because I don't think that is where your error is occurring since it was successful on my systems with the same code.

Regards,

Ryan Crosby

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

One older post does suggest that you cannot use the GlobalContainer while trying to view queues.  Are you doing a test of the mapping or are you trying to simply display the queues?

Regards,

Ryan Crosby

Former Member
0 Kudos

Thanks a 1000 bunch Ryan. You nailed it down on the reply quoted below.

"

Hi Mike,

One older post does suggest that you cannot use the GlobalContainer while trying to view queues.  Are you doing a test of the mapping or are you trying to simply display the queues?

   Regards,

Ryan Crosby

I was using display queue but the target field carried the value when I tested the mapping...thanks again. Now, I have to use one more similar hashmap for the material internal id. Is that right? Or you would suggest to use the method you pointed (You could also use a HashSet for getting your two types of values but that would require generating the imported archive for a class that would implement the Comparable interface for Java. ) to do in one shot for both material internal Id and tuple value? Again thanks a heavy bunch.....Marking it as correct answer. But would really appreciate if you could help me for the hashshet and imported archive for a class atleast for future reference

you are absolutely awesome and great great help to this community....I haven't seen anyone so diligently helping to reach the solution..hats off.

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

You're welcome for the help, I try to keep the skills sharp by helping on the forum and glad that it was able to work for your scenario.  I checked the HashSet implementation and that one is not as friendly because you would have to always create an object of the class in the imported archive every time you want to see if it exists in the set (call new MaterialData("someid"); for every time you want to look it up).  For this reason the HashMap will be easier so here is the basic code you can compile and import into your system as an imported archive.  Then you only need to add it as an import into your map:


import java.util.ArrayList;

public class MaterialData {

   

    private String matIntID;

    private ArrayList<String> tuples;

    public MaterialData(){}

    public void setMatIntID(String matIntID){ this.matIntID = matIntID; }

    public void setTuples(ArrayList<String> tuples){ this.tuples = tuples; }

   

    public String getMatIntID(){ return matIntID; }

    public ArrayList<String> getTuples(){ return tuples; }

}

I did not compile it because the .jar you create will be specific to whichever Java version you are running on your PO installation.  So you can import this into NWDS or Eclipse and then export it to a .jar file.

Regards,

Ryan Crosby

Former Member
0 Kudos

Well, how does it all fit together? I am not able to visualize end to end

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

I can help you with that part too.  Does your soapLookup get only the tuples or does it retrieve the internal material IDs also?

Regards,

Ryan Crosby

Former Member
0 Kudos

soapLookup gets both. So thats why I am trying to avoid calling webservice twice instead of just one

thx

mike

Ryan-Crosby
Active Contributor
0 Kudos

Hi Mike,

So the insertion into the HashMap would be pretty staightforward but you could have a couple of options for the retrieval depending on how you want to handle it.  Code for the insertion:


HashMap<String, MaterialData> hm = new HashMap<String, MaterialData>();

String matID = someValue;

ArrayList<String> al = new ArrayList<String>();

String intMatID = someValueFromSoap;

// .. adding entries to ArrayList here and getting the internal mat id

MaterialData md = new MaterialData();

md.setMatIntID(intMatID);

md.setTuples(al);


hm.put(matID, md);

So for retrieving values you could have one more generic UDF for both field mappings or two UDFs with each one being specific to the target field.  Here is what the generic one could look like:


HashMap<String, MaterialData> hm = (HashMap<String, MaterialData>) container.getGlobalContainer().getParameter("matmap");

String targetField = target[0];

for(int i = 0; i < coreid.length; i++)

{

MaterialData md = hm.get(coreid[i]);

if(target[0].equals("intMatID")

result.addValue(md.getIntMatID());

else if(target[0].equals("tuples"))

{

ArrayList<String> al = md.getTuples();

for(int j = 0; j < al.size(); j++)

{

result.addValue(al.get(j));

}

}

else

// throw some error - should never happen except for at build time

}

The one thing to note in this example is that you would need an extra parameter along with the matID value to determine which field of your MaterialData object you want to add to the ResultList object.  The only difference for two separate UDFs is that the logic inside the loop from lines 05-17 above would be specific to the UDF and whichever target field that it would be mapped to, i.e. the internal mat ID would have the simple assignment whereas the tuple requires the extra loop to output all values.

Regards,

Ryan Crosby

Former Member
0 Kudos

Great ....!!! I will try and let you know the result soon .....

thx

mike

Former Member
0 Kudos

Ryan

some questions to debug because I am unable to get desired result after putting this altogether

1) how do I put trace info for seeing values in mattuple?

matTuple.put(material_id, md);

trace.addInfo("mattuple " + matTuple);

md is the Material Data object and mattuple is the hashmap

the above trace is not giving me values for md. It's giving as

  • mattuple {123=com.mdt.hashmapfortuples.MaterialData@1697bc3c, 456=com.mdt.hashmapfortuples.MaterialData@1697bc3c, 789=com.mdt.hashmapfortuples.MaterialData@1697bc3c}

123, 456 and 789 are material IDs in my case

Until I see what is populated in the mattuple, I can't really understand if the values for matINTID and tuples are correctly transferred to the object 'md' of 'Material Data' class. 

2) when I put trace in the extract hashmap function, I get the value as below for the hashmap.

  • matmap1{123=com.mdt.hashmapfortuples.MaterialData@1697bc3c, 456=com.mdt.hashmapfortuples.MaterialData@1697bc3c, 789=com.mdt.hashmapfortuples.MaterialData@1697bc3c}

this is the code

HashMap<String, MaterialData> matmap1 =(HashMap<String, MaterialData>) container.getGlobalContainer().getParameter("matmap");

AbstractTrace trace = container.getTrace();

trace.addInfo("matmap1" + matmap1);

In the target structure, for 3 material Ids , I am always getting the last material ID internal Id for all the 3 target Material node and last material tuple Id as well

thx

mike