Posts tagged ‘Mapping’

Developing Java Mappings for SAP XI

You can use NetBeans to develop java mappings for the SAP NetWeaver Exchange Infrastructure (SAP XI). The only thing you need is the mapping api library from your SAP XI installation.

Registering Mapping API in NetBeans

Use the NetBeans Library Manager under «Tools->Library Manager» to register the aii_map_api.jar file. You find this library in the following path of your SAP XI installation:

<SAP_install_dir>/<system_name>/<instance_name>/j2ee/cluster/
server<number>/apps/sap.com/com.sap.xi.services/

Project settings

The SAP NetWeaver Exchange Infrastructure 3.0 is based on J2EE 1.3 and Java 1.4, so you must use a JDK 1.4.x and source level 1.4 for your project.

Useful libraries

SAP XI uses XML messages for message exchange. You can eather use the standard XML api libraries shipped with the JDK or you can use the pretty nice JDom open source library. I preffer JDom, because it has a build-in SAX builder and can also output XML as text (also formatted).

Mapping class

Your mapping class must implement the com.sap.aii.mapping.api.StreamTransformation interface. This interface requires you to implement the execute() and setParameter() methods.

Method execute()

The execute() method does the whole mapping. This method has two parameters of type java.io.InputStream and java.io.OutputStream. You read data from the input stream, make the necessary mapping and write the resulting data to the output stream.

SAP XI usually uses XML messages, but you can also read and write other type of data in your mapping, e.g. comma-separated values.

Method setParameter()

The integration engine uses the method setParameter() to inform the mapper about runtime configuration parameters. You can use the constants of the interface com.sap.aii.mapping.api.StreamTransformationConstants to access this parameters.

Sample mapping class for IDOC to file scenario

In this sample we map an IDOC to a plain text file.

XML representation of this IDOC

<?xml version="1.0" encoding="UTF-8" ?>
<ZADDRESS01>
  <IDOC BEGIN="1">
    <EDI_DC40 SEGMENT="1">
      <TABNAM>EDI_DC40</TABNAM>
      <MANDT>100</MANDT>
      <DOCNUM>0000000000000001</DOCNUM>
      <DOCREL>620</DOCREL>
      <STATUS>30</STATUS>
      <DIRECT>1</DIRECT>
      <OUTMOD>4</OUTMOD>
      <IDOCTYP>ZADDRESS01</IDOCTYP>
      <MESTYP>ZADDRESS</MESTYP>
      <MESCOD>DEV</MESCOD>
      <MESFCT>OUT</MESFCT>
      <SNDPOR>SAPDEV</SNDPOR>
      <SNDPRT>LS</SNDPRT>
      <SNDPRN>DEVCLNT100</SNDPRN>
      <RCVPOR>XID</RCVPOR>
      <RCVPRT>LS</RCVPRT>
      <RCVPRN>PARTNER1</RCVPRN>
      <CREDAT>20071028</CREDAT>
      <CRETIM>101658</CRETIM>
      <SERIAL>20071028101657</SERIAL>
    </EDI_DC40>
    <ZADDRESS SEGMENT="1">
      <VKORG>1000</VKORG>
      <TITLE>Mr.</TITLE>
      <LASTNAME>Doe</LASTNAME>
      <FIRSTNAME>John</FIRSTNAME>
      <STREET>Pier</STREET>
      <STREETNO>47</STREETNO>
      <CITY>San Francisco</CITY>
      <POSTLCODE>94133</POSTLCODE>
      <STATE>CA</STATE>
      <COUNTRY>US</COUNTRY>
    </ZADDRESS>
    <ZADDRESS SEGMENT="1">
      <VKORG>1000</VKORG>
      <TITLE>Mrs.</TITLE>
      <LASTNAME>Doe</LASTNAME>
      <FIRSTNAME>Jane</FIRSTNAME>
      <STREET>Mason St.</STREET>
      <STREETNO>950</STREETNO>
      <CITY>San Francisco</CITY>
      <POSTLCODE>94108</POSTLCODE>
      <STATE>CA</STATE>
      <COUNTRY>US</COUNTRY>
    </ZADDRESS>
  </IDOC>
</ZADDRESS01>

Source code of mapper:

  1. package de.gascoyne.mapper;
  2.  
  3. import com.sap.aii.mapping.api.StreamTransformation;
  4. import com.sap.aii.mapping.api.StreamTransformationConstants;
  5. import com.sap.aii.mapping.api.StreamTransformationException;
  6. import java.io.InputStream;
  7. import java.io.OutputStream;
  8. import java.io.PrintWriter;
  9. import java.util.Iterator;
  10. import java.util.Map;
  11. import org.jdom.Document;
  12. import org.jdom.Element;
  13. import org.jdom.input.SAXBuilder;
  14.  
  15. /**
  16.  * Sample mapper for SAP-XI
  17.  * @author Marcel Gascoyne
  18.  */
  19. public class AddressToTextMapper implements StreamTransformation {
  20.  
  21. private String party = "";
  22.  
  23. /**
  24.   * Injection of mapping parameters
  25.   * from integration engine
  26.   *
  27.   * @param map Map with configuration data
  28.   */
  29. public void setParameter(Map map) {
  30. // Determine receiver party
  31. party = (String) map.get(
  32. StreamTransformationConstants.RECEIVER_PARTY );
  33. }
  34.  
  35. /**
  36.   * Mapping implementation
  37.   *
  38.   * @param inputStream Input data from integration engine
  39.   * @param outputStream Output data to integration engine
  40.   */
  41. public void execute(InputStream inputStream,
  42. OutputStream outputStream)
  43. throws StreamTransformationException {
  44. try {
  45. // get idoc from input stream
  46. SAXBuilder sb = new SAXBuilder();
  47. Document doc = sb.build( inputStream );
  48. Element idoc = doc.getRootElement().getChild( "IDOC" );
  49.  
  50. // create PrintWriter for output stream
  51. PrintWriter out = new PrintWriter( outputStream );
  52. String header = "party;vkorg;title;lastname;firstname;" +
  53. "street;streetno;city;postlcode;state;country\n";
  54. out.print( header );
  55.  
  56. // process all ZADDRESS segments
  57. for ( Iterator it = idoc.getChildren().iterator(); it.hasNext(); ) {
  58. Element element = (Element) it.next();
  59.  
  60. if ( !element.getName().equals( "ZADDRESS" ) ) {
  61. continue;
  62. }
  63.  
  64. // get data from segment ZADDRESS
  65. String vkorg = element.getChildText( "VKORG" );
  66. String title = element.getChildText( "TITLE" );
  67. String lastname = element.getChildText( "LASTNAME" );
  68. String firstname = element.getChildText( "FIRSTNAME" );
  69. String street = element.getChildText( "STREET" );
  70. String streetNo = element.getChildText( "STREETNO" );
  71. String city = element.getChildText( "CITY" );
  72. String postlCode = element.getChildText( "POSTLCODE" );
  73. String state = element.getChildText( "STATE" );
  74. String country = element.getChildText( "COUNTRY" );
  75.  
  76. // create output line
  77. String line =
  78. vkorg + ";" +
  79. title + ";" +
  80. lastname + ";" +
  81. firstname + ";" +
  82. street + ";" +
  83. streetNo + ";" +
  84. city + ";" +
  85. postlCode + ";" +
  86. state + ";" +
  87. country + "\r\n";
  88.  
  89. // Write line to output stream
  90. out.print( line );
  91.  
  92. }
  93.  
  94. out.flush();
  95. out.close();
  96.  
  97. } catch ( Exception e ) {
  98. throw new StreamTransformationException( e.getMessage() );
  99. }
  100. }
  101.  
  102. }

Resulting text file:

party;vkorg;title;lastname;firstname;street;streetno;city;postlcode;state;country
PARTNER1;1000;Mr.;Doe;John;Pier;47;San Francisco;94133;CA;US
PARTNER1;1000;Mrs.;Doe;Jane;Mason St.;950;San Francisco;94108;CA;US

Sample mapping class for file to IDOC scenario

Now the opposite way. In this scenario we map a text file to an IDOC. We must create the whole IDOC structure from scratch in XML format. Many fields in the IDOC control record are mandantory fields and must be set, although the integration engine injects the correct values in this fields. We set this fields to the static text empty and the integration engine do the rest.

Input text file:

party;vkorg;title;lastname;firstname;street;streetno;city;postlcode;state;country
1000;Mr.;Doe;John;Pier;47;San Francisco;94133;CA;US
1000;Mrs.;Doe;Jane;Mason St.;950;San Francisco;94108;CA;US

Source code of java mapper:

  1. package de.gascoyne.mapper;
  2.  
  3. import com.sap.aii.mapping.api.StreamTransformation;
  4. import com.sap.aii.mapping.api.StreamTransformationConstants;
  5. import com.sap.aii.mapping.api.StreamTransformationException;
  6. import java.io.BufferedReader;
  7. import java.io.InputStream;
  8. import java.io.InputStreamReader;
  9. import java.io.OutputStream;
  10. import java.io.PrintWriter;
  11. import java.text.SimpleDateFormat;
  12. import java.util.Date;
  13. import java.util.Map;
  14. import org.jdom.Document;
  15. import org.jdom.Element;
  16. import org.jdom.output.XMLOutputter;
  17.  
  18. /**
  19.  * Sample mapper for SAP-XI
  20.  * @author Marcel Gascoyne
  21.  */
  22. public class FileToIdocMapper implements StreamTransformation {
  23.  
  24. private String party = "";
  25.  
  26. /**
  27.   * Injection of mapping parameters
  28.   * from integration engine
  29.   *
  30.   * @param map Map with configuration data
  31.   */
  32. public void setParameter(Map map) {
  33. // Determine receiver party
  34. party = (String) map.get(
  35. StreamTransformationConstants.RECEIVER_PARTY );
  36. }
  37.  
  38. /**
  39.   * Mapping implementation
  40.   *
  41.   * @param inputStream Input data from integration engine
  42.   * @param outputStream Output data to integration engine
  43.   */
  44. public void execute(InputStream inputStream,
  45. OutputStream outputStream)
  46. throws StreamTransformationException {
  47. try {
  48. InputStreamReader ir = new InputStreamReader( inputStream, "ISO-8859-1" );
  49.  
  50. // create control record
  51. SimpleDateFormat df = new SimpleDateFormat( "yyyyMMddHHmmss" );
  52. Element edi_dc40 = new Element( "EDI_DC40" );
  53. edi_dc40.setAttribute( "SEGMENT", "1" );
  54. edi_dc40.addContent( new Element( "TABNAM" ).addContent( "empty" ) );
  55. edi_dc40.addContent( new Element( "DIRECT" ).addContent( "empty" ) );
  56. edi_dc40.addContent( new Element( "IDOCTYP" ).addContent( "empty" ) );
  57. edi_dc40.addContent( new Element( "MESTYP" ).addContent( "empty" ) );
  58. edi_dc40.addContent( new Element( "SNDPOR" ).addContent( "empty" ) );
  59. edi_dc40.addContent( new Element( "SNDPRT" ).addContent( "empty" ) );
  60. edi_dc40.addContent( new Element( "SNDPRN" ).addContent( "empty" ) );
  61. edi_dc40.addContent( new Element( "RCVPOR" ).addContent( "empty" ) );
  62. edi_dc40.addContent( new Element( "RCVPRN" ).addContent( "empty" ) );
  63. edi_dc40.addContent( new Element( "SERIAL" ).addContent( df.format( new Date() ) ) );
  64.  
  65. // create idoc
  66. Document zaddress01 = new Document();
  67. Element root = new Element( "ZADDRESS01" );
  68. zaddress01.setRootElement( root );
  69. Element idoc = new Element( "IDOC" );
  70. idoc.setAttribute( "BEGIN", "1" );
  71. idoc.addContent( edi_dc40 );
  72. root.addContent( idoc );
  73.  
  74. while ( br.ready() ) {
  75. // get line of input stream
  76. String line = br.readLine();
  77.  
  78. // check for header line
  79. if ( line.startsWith( "party;vkorg;" ) ) {
  80. continue;
  81. }
  82.  
  83. // split values by semicolon in array
  84. String[] columns = line.split( ";" );
  85.  
  86. // create ZADDRESS segment
  87. Element zaddress = new Element( "ZADDRESS" );
  88. zaddress.setAttribute( "SEGMENT", "1" );
  89. zaddress.addContent( new Element( "VKORG" ).setText( columns[1] ) );
  90. zaddress.addContent( new Element( "TITLE" ).setText( columns[2] ) );
  91. zaddress.addContent( new Element( "LASTNAME" ).setText( columns[3] ) );
  92. zaddress.addContent( new Element( "FIRSTNAME" ).setText( columns[4] ) );
  93. zaddress.addContent( new Element( "STREET" ).setText( columns[5] ) );
  94. zaddress.addContent( new Element( "CITY" ).setText( columns[6] ) );
  95. zaddress.addContent( new Element( "POSTLCODE" ).setText( columns[7] ) );
  96. zaddress.addContent( new Element( "STATE" ).setText( columns[8] ) );
  97. zaddress.addContent( new Element( "COUNTRY" ).setText( columns[9] ) );
  98.  
  99. // add segment to idoc
  100. idoc.addContent( zaddress );
  101. }
  102.  
  103. // write idoc to output stream
  104. PrintWriter out = new PrintWriter( outputStream );
  105. XMLOutputter xout = new XMLOutputter();
  106. xout.output( zaddress01, out );
  107.  
  108. } catch ( Exception e ) {
  109. throw new StreamTransformationException( e.getMessage() );
  110. }
  111. }
  112.  
  113. }

Unit tests

To locally test your java mappings, you can create JUnit tests inside the NetBeans IDE. From within your mapping class select «Tools->Create JUnit Tests» to create a test case for your mapper.

This is a sample test case for an idoc-to-file mapper:

  1. package de.gascoyne.mapper;
  2.  
  3. import com.sap.aii.mapping.api.StreamTransformationConstants;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import junit.framework.*;
  7. import java.io.InputStream;
  8. import java.io.OutputStream;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11.  
  12. /**
  13.  * TestCase for AddressToTextMapper
  14.  * @author Marcel Gascoyne
  15.  */
  16. public class AddressToTextMapperTest extends TestCase {
  17.  
  18. public void testExecute() throws Exception {
  19. InputStream in = new FileInputStream( "build/test/classes/idoc_in.xml" );
  20. OutputStream out = new FileOutputStream( "build/test/results/text_out.txt" );
  21. AddressToTextMapper instance = new AddressToTextMapper();
  22. Map params = new HashMap();
  23.  
  24. params.put( StreamTransformationConstants.RECEIVER_PARTY, "PARTY1" );
  25. instance.setParameter( params );
  26. instance.execute( in, out );
  27. }
  28.  
  29. }