cancel
Showing results for 
Search instead for 
Did you mean: 

RFC Lookup with XSLT: ArrayIndexOutOfBoundsException

peter_wallner2
Active Contributor
0 Kudos

Dear experts,

I am doing an RFC lookup from XSLT mapping.

I am mapping to an ORDERS iDoc and I need to look up values in table MVKE for each position of the iDoc.

So for each material the lookup needs to be done.

I got the Java code from this page:

http://www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/05a3d62e-0a01-0010-14bc-adc8efd4e...

If I only have one material in my source XML and create an iDoc it is working fine, the correct MVKE value I need is looked up.

If I have more than one position (more than one material) in the source XML I get the error:

javax.xml.transform.TransformerException: java.lang.ArrayIndexOutOfBoundsException: 5930

at com.sap.engine.lib.jaxp.TransformerImpl.transform(TransformerImpl.java:250)

at com.sap.aii.ib.server.mapping.execution.AbstractMappingTransformer.transform(AbstractMappingTransformer.java:174)

at com.sap.aii.ib.server.mapping.execution.XSLTMapping.executeStep(XSLTMapping.java:80)

at com.sap.aii.ib.server.mapping.execution.Mapping.execute(Mapping.java:60)

at com.sap.aii.ib.server.mapping.execution.SequenceMapping.executeStep(SequenceMapping.java:40)

.....

What am I doing wrong?

Thank you for any idea on this.

Best regards,

Peter

Accepted Solutions (1)

Accepted Solutions (1)

0 Kudos

Hello Peter,

I had the same problem like you had.

The main Problem is that with the example code the request variable is created as NodeList object. In XSLT a variable is somekind of a constant and can't be changed. As the request object is empty after the first request the programm fails at the following line:

Source source = new DOMSource(request.item(0));

So I've created a workaround for this problem.

In the call of the template I've put the request as a parameter object at the template call:

<xsl:with-param name="req">

<rfc:PLM_EXPLORE_BILL_OF_MATERIAL xmlns:rfc="urn:sap-com:document:sap:rfc:functions">

  <APPLICATION>Z001</APPLICATION>

  <FLAG_NEW_EXPLOSION>X</FLAG_NEW_EXPLOSION>

  <MATERIALNUMBER><xsl:value-of select="value"/></MATERIALNUMBER>

  <PLANT>FSD0</PLANT>

  <VALIDFROM><xsl:value-of select="//Recordset/Row[name='DTM-031']/value"/></VALIDFROM>

  <BOMITEM_DATA/>

</rfc:PLM_EXPLORE_BILL_OF_MATERIAL>

</xsl:with-param>

With this change the request will be provided as a String object and not as a NodeList object.

Afterwards the RfcLookup.java has to be changed to the following:

package com.franke.mappings;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;

import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.sap.aii.mapping.lookup.Channel;
import com.sap.aii.mapping.api.StreamTransformationConstants;
import com.sap.aii.mapping.api.AbstractTrace;
import com.sap.aii.mapping.lookup.RfcAccessor;
import com.sap.aii.mapping.lookup.LookupService;
import com.sap.aii.mapping.lookup.XmlPayload;


/**
* @author Thorsten Nordholm Søbirk, AppliCon A/S
*
* Helper class for using the XI Lookup API with XSLT mappings for calling RFCs.
* The class is generic in that it can be used to call any remote-enabled
* function module in R/3. Generation of the XML request document and parsing of
* the XML response is left to the stylesheet, where this can be done in a very
* natural manner.
*
* TD:
* Changed the class that request is sent as String, because of IndexOutOfBound-exception
* When sending multiple requests in one XSLT mapping.
*
*/
public class RfcLookup {
    /**
    * Execute RFC lookup.
    * @param request RFC request - TD: changed to String
    * @param service name of service
    * @param channelName name of communication channel
    * @param inputParam mapping parameters
    * @return Node containing RFC response
    */
    public static Node execute( String request,
            String service,
            String channelName,
            Map inputParam)

    {
          AbstractTrace trace = (AbstractTrace) inputParam.get(StreamTransformationConstants.MAPPING_TRACE);
          Node responseNode = null;


          try {
              // Get channel and accessor
              Channel channel = LookupService.getChannel(service, channelName);
              RfcAccessor accessor = LookupService.getRfcAccessor(channel);

 

              // Serialise request NodeList - TD: Not needed anymore as request is String
              /*TransformerFactory factory = TransformerFactory.newInstance();
              Transformer transformer = factory.newTransformer();

              Source source = new DOMSource(request.item(0));
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
              StreamResult streamResult = new StreamResult(baos);
              transformer.transform(source, streamResult);*/

 

                // TD: Add xml header and remove linefeeds for the request string
                request = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+request.replaceAll("[\r\n]+", ""); 
 
                // TD: Get byte Array from request String to send afterwards
                byte[] requestBytes = request.getBytes();
 
              // TD: Not used anymore as request is String
                //byte[] requestBytes = baos.toByteArray();

                trace.addDebugMessage("RFC Request: " + new String(requestBytes));

                // Create input stream representing the function module request message
                InputStream inputStream = new ByteArrayInputStream(requestBytes);

             

                // Create XmlPayload
                XmlPayload requestPayload =LookupService.getXmlPayload(inputStream);

                // Execute lookup
                XmlPayload responsePayload = accessor.call(requestPayload);
                InputStream responseStream = responsePayload.getContent();
                TeeInputStream tee = new TeeInputStream(responseStream);

                // Create DOM tree for response
                DocumentBuilder docBuilder =DocumentBuilderFactory.newInstance().newDocumentBuilder();
                Document document = docBuilder.parse(tee);
                trace.addDebugMessage("RFC Response: " + tee.getStringContent());
                responseNode = document.getFirstChild();

          } catch (Throwable t) {
              StringWriter sw = new StringWriter();
              t.printStackTrace(new PrintWriter(sw));
              trace.addWarning(sw.toString());
          }
          return responseNode;

    }
    /**
    * Helper class which collects stream input while reading.
    */
    static class TeeInputStream extends InputStream {
          private ByteArrayOutputStream baos;
          private InputStream wrappedInputStream;

          TeeInputStream(InputStream inputStream) {
                baos = new ByteArrayOutputStream();
                wrappedInputStream = inputStream;
          }
          /**
          * @return stream content as String
          */

          String getStringContent() {
                return baos.toString();

          }
          /* (non-Javadoc)
          * @see java.io.InputStream#read()
          */
          public int read() throws IOException {
              int r = wrappedInputStream.read();
              baos.write(r);
              return r;
        }
    }
}

Then you need to compile and upload this class and it should work.

I hope that this helps you.

Best regards

Till

peter_wallner2
Active Contributor
0 Kudos

Hello Till,

Thank you for your input. I did not expect another answer to this. Like I said I used a graphical mapping at the end using the graphical RFC Lookup function.

When I have time I really want to try your solution and see if it works. Thank you for the detailed description. I do a lot of XSL and know that an XSLT variable can't be changed once defined. But I did not see the connection here in this example.... thank you!

I marked your answer as "helpful" for now and once I have tried your solution and it works I will mark it as "Correct answer".

Best regards,

Peter

peter_wallner2
Active Contributor
0 Kudos

Hello Till,

your Java code did the magic! Thank you for this answer - I really appreciate it. With your solution it is possible to do multiple RFC requests from an XSLT mapping without the need of a graphical mapping.

Best regards,

Peter

0 Kudos

Hello Peter,

Thanks a lot for your tests and feedback.

Best regards

Till

Answers (1)

Answers (1)

baskar_gopalakrishnan2
Active Contributor
0 Kudos

The above details does not help to shed more light on this issue. Please check your logic where you use or pass array variable like string or integer and see whether you  access or retrieve value out of index from it.

peter_wallner2
Active Contributor
0 Kudos

Hello Baskar,

I am not sure what the response I get from RFC looks like.

My RFC looks like this:

SELECT SINGLE * FROM mvke INTO MVKE_LINE

    WHERE matnr = id_material

    AND vtweg   = '01'.

My XSLT looks like this. So in the source I have multiple positions and whenever one position is "matched" the iDoc values are created and also the "getMVKE" is called:

<xsl:template match="positions">

...

<xsl:call-template name="getMVKE">

      <xsl:with-param name="materialID" select="$material"/>

</xsl:call-template>

...

</xsl:template>

Then the template "getMVKE":

<xsl:template name="getMVKE">

        <xsl:variable name="materialID"/>

        <xsl:call-template name="lookupMVKE">

            <xsl:with-param name="materialID" select="$materialID"/>

        </xsl:call-template>

    </xsl:template>

And the Look up template:

<xsl:template name="lookupMVKE">

        <xsl:param name="materialID"/>

        <xsl:variable name="requestmvke">

            <rfc:Z_XI_MICROS_AU_MVKE>

                <ID_MATERIAL>

                    <xsl:value-of select="$materialID"/>

                </ID_MATERIAL>

            </rfc:Z_XI_MICROS_AU_MVKE>

        </xsl:variable>

        <xsl:variable name="responsemvke" select="lookup:execute($requestmvke, 'BS_T_AU_ERP', 'CC_RFC_ADAPTER_IN', $inputparam)"/>   

        <xsl:element name="MVKE">

            <xsl:value-of select="$responsemvke//MTPOS"/>

        </xsl:element>

    </xsl:template>

So from my understanding my ABAP RFC coding only returns 1 value per call.

Using XPI-Inspector I see the RFC request which looks like this:

RFC Request: <?xml version="1.0" encoding="utf-8"?>

<rfc:Z_XI_MICROS_AU_MVKE xmlns:rfc="urn:sap-com:document:sap:rfc:functions"><ID_MATERIAL>000000000000002083</ID_MATERIAL></rfc:Z_XI_MICROS_AU_MVKE>

and the response looks like this:

<?xml version="1.0" encoding="UTF-8"?><rfc:Z_XI_MICROS_AU_MVKE.Response xmlns:rfc="urn:sap-com:document:sap:rfc:functions"><MVKE_LINE><MANDT>112</MANDT><MATNR>000000000000002083</MATNR><VKORG>.............

and then comes the OutOfBoundsException.

Where should I check my logic? I do not understand your answer.

Best regards,

Peter

baskar_gopalakrishnan2
Active Contributor
0 Kudos

>Where should I check my logic? I do not understand your answer.

The exception comes from java logic. You need to check over there and see how do you define array object over there.

peter_wallner2
Active Contributor
0 Kudos

Hello Baskar,

Thank you for your patience. In the blog by Thorsten Nordholm Søbirk the response is

made by:

...

// Create DOM tree for response

DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

Document document = docBuilder.parse(tee);

trace.addDebugMessage("RFC Response: " + tee.getStringContent());

responseNode = document.getFirstChild();

....

return responseNode;

But there is no Array that he loops through. Can you please help again - thank you.

Best regards,

Peter

baskar_gopalakrishnan2
Active Contributor
0 Kudos

>But there is no Array that he loops through. Can you please help again - thank you.

It's quite bit to explain. The java code might use api example which calls other object instance where it might use string array object and it could fail due to indexoutofbound exception. The best way is to get the data and debug the code using eclipse or developer studio and see the debug log traces to find the error and its cause.  Since you have already mentioned that if the source xml contains a material then you dont get the exception and only more than one material causing this. You can copy the data of both instances run against this code using eclipse debug mode or so and find more details about this. This is one possible approach. Take help from Java team if you need to debug the code.

peter_wallner2
Active Contributor
0 Kudos

Hello Baskar,

We also tried now to just do one RFC call from the XSLT but we also received the "ArrayIndexOutOfBoundsException". My colleagues think that the Java API called by XSLT has a problem when the RFC returns multiple values. They said the Java API that the graphical mapping is using is more reliable.

Now I added a graphical mapping at the end that maps an iDoc to an iDoc 1:1 but for one element (PSTYV) the RFC lookup using the graphical RFC mapping function is executed.

Now it works perfectly.

Thank you for your help and best regards,

Peter