Message payload encoded in Base64

Here is a simple step-by-step guide on how to deal with messages encoded in Base64.
Below tutorial is based on PROXY-to-SOAP synchronous scenario in which SAP BW consumes data from WebService provided by 3rd Party. Response message from 3rd Party system comes as encoded in Base64 XML message while consumer system requires plain XML message.

Development of such scenario consists of following parts:

  1. System Landscape Directory (SLD) development
  2. Enterprise Services Repository (ESR) development
    2a. Custom Java Mapping development
  3. Integration Directory (ID) development

Ad 1. Create Products, Technical Systems and Business System according to your requirements.

Ad 2. In ESR create following objects:
       2.1. Data Types for request and resonse messages
       2.2. Required Message Types
       2.3. Message Mapping between source and destination request messages

Source Message Mapping
In my case source and target request message are the same so there’s nothing really interesting hence only a screenshot giving you an idea of the request message structure and mappings:

request_mapping
Request message mapping

       2.4. Message Mapping between source and destination response messages

Response Message Mapping
In my case response mapping is handled by custom Java mapping class. Complete custom Java mapping class code has been attached at the end of the note as Appendix 1.

2.5. Message Interface

Create 2 message interface objects:

Outbound, synchronous interface for sender (in my case this one represents SAP BW)

MI_OB

And inbound, synchronous one for receiver (this one represents 3rd Party WS)

MI_IB

2.5. Interface Mapping

Once you’re done with creating message interface objects which are again:

  • 2 Data types for requests
  • 2 Data types for responses
  • 1 request message mapping object
  • 1 custom java class to map response messages
  • 2 message interfaces: inbound and outbound, both synchronous

you are ready to finalize development in Enterprise Service Repository by creating interface mapping object where you put all objects you have created before all together.

As presented in below screenshot, mapping program has been marked green. It is custom Java class responsible for mapping response message from 3rd Party WebService to desired format by consumer system which, in my case, is SAP BW.

Interface Mapping (response)

Ad 3. In Integration Directory  make sure to create required communication channels. In my case it’s sender and receiver XI adapter channels for SAP BW communication and SOAP receiver channel for 3rd Party-provided WebService.

Then create Integrated Configuration and voila – you’re done. Now the most pleasant part where all your hard work comes to fruition – testing! 🙂


Appendix 1.

Below you can see how the interface works:

request
Picture 1. Request message

2.

resp_before
Picture 2. Response before mapping

3.

resp_after
Picture 3. Response after mapping

Appendix 2.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.util.Map;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import sun.misc.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sap.aii.mapping.api.StreamTransformation;
import com.sap.aii.mapping.api.StreamTransformationException;

/********************************************************
*
* @author Maciej Białek, Marzec 2014
*
*
*
* Opis algorytmu:
*
* Mapowanie odbywa się od stworzenia pustej struktury komunikatu docelowego oraz odkodowaniu
* fragmentu komunikatu przysłanego z systemu źródłowego w formacie Base64. Odkodowane komunikaty
* przeszukiwane sa pojedynczo pod katem wezlow wystepujacaych w komunikacie docelowym.
* Znalezione wartosci wstawiane sa nastepnie we wlasciwe miejsce komunikatu docelowego.
* Niektore timestampy dzielone sa na czesc zawierajaca date oraz godzine i wstawiane w dedykowane
* pola komunikatu docelowego.
*
*/

public class SAXmapping implements StreamTransformation {

String result = “”;

/***************************************************************************************/
/** Metoda testowa zawierajaca testowe wywolanie metody execute
*
* @param args
*/
public static void main(String[] args) {

SAXmapping saxMapping = new SAXmapping();

try{
FileInputStream in = new FileInputStream(“C:/Users/IBM_ADMIN/workspace/SOWE2BW_mapping/test.xml”);
FileOutputStream out = new FileOutputStream(“C:/Users/IBM_ADMIN/workspace/SOWE2BW_mapping/out.xml”);
saxMapping.execute(in, out);
}
catch(Exception e){
e.printStackTrace();
}
}
/***************************************************************************************/
/** Metoda wywoływana przez SAP PI do mapowania pol pomiedzy komunikatem zrodlowym a docelowym
*
*/
public void execute(InputStream in, OutputStream out)
throws StreamTransformationException {

try {

String data = null;
String encodedString, decodedString = null;

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(in);

Node node = null;
NodeList l = doc.getElementsByTagName(“xml”);

// tworzymy strukture docelowego komunikatu
Document finaldoc = buildOutputXML(l.getLength());

for(int i = 0 ; i < l.getLength() ; ++i){

/**
* Zaciagamy do pamieci i rozszyfrowujemy komunikat w base64 (pojedynczo)
*/
node = l.item(i);

if (node.getNodeType() == Node.ELEMENT_NODE) {

encodedString = l.item(i).getChildNodes().item(0).getNodeValue();

BASE64Decoder decoder = new BASE64Decoder();
byte[] decodedBytes = decoder.decodeBuffer(encodedString);
decodedString = new String(decodedBytes, “ISO-8859-2”);

System.out.println(decodedString);
}

/**
* Zaczytujemy xml zrodlowy jako String do obiektu Document
*/

StringReader sr_src = new StringReader(decodedString);
InputSource is_src = new InputSource(sr_src);
Document source = builder.parse(is_src);

/**
* Wyszukujemy poszczegolnych elementow wiadomosci zrodlowej i mapujemy je na elementy wiadomosci docelowej
*/

data = getSourceTagValue(source, “kod_kom”);
setTagValue(finaldoc, i ,”KODKO” , data );
data = “”;

data = getSourceTagValue(source, “data_utworzenia”);
setTagValue(finaldoc, i ,”TIMST” , data );
data = “”;

data = getSourceTagValue(source, “id”);
setTagValue(finaldoc, i ,”ID” , data );
data = “”;

data = getSourceTagValue(source, “JDU”);
setTagValue(finaldoc, i ,”JDU” , data );
data = “”;

data = getSourceTagValue(source, “KJWCD”);
setTagValue(finaldoc, i ,”BLOK” , data );
data = “”;

data = getSourceTagValue(source, “DCZ”);
setTagValue(finaldoc, i ,”DATE0″ , getDate(data) );
setTagValue(finaldoc, i ,”TIME” , getTime(data) );
data = “”;

data = getSourceTagValue(source, “ZD”);
setTagValue(finaldoc, i ,”ZD” , data );
data = “”;

data = getSourceTagValue(source, “TST”);
setTagValue(finaldoc, i ,”TST” , data );
data = “”;

data = getSourceTagValue(source, “KWPR”);
setTagValue(finaldoc, i ,”KWPR” , data );
data = “”;

data = getSourceTagValue(source, “TSW”);
setTagValue(finaldoc, i ,”TSW” , data );
data = “”;

data = getSourceTagValue(source, “KWPO”);
setTagValue(finaldoc, i ,”KWPO” , data );
data = “”;

data = getSourceTagValue(source, “ZS”);
setTagValue(finaldoc, i ,”ZS” , data );
data = “”;

data = getSourceTagValue(source, “POBC”);
setTagValue(finaldoc, i ,”POBC” , data );
data = “”;

data = getSourceTagValue(source, “TU”);
setTagValue(finaldoc, i ,”TU” , data );
data = “”;

data = getSourceTagValue(source, “TR”);
setTagValue(finaldoc, i ,”TR” , data );
data = “”;

data = getSourceTagValue(source, “ZRD”);
setTagValue(finaldoc, i ,”ZRD” , data );
data = “”;

data = getSourceTagValue(source, “DOW”);
if(data != “”){
setTagValue(finaldoc, i ,”DATE0″ , getDate(data) );
setTagValue(finaldoc, i ,”TIME” , getTime(data) );
}
data = “”;

data = getSourceTagValue(source, “DDW”);
setTagValue(finaldoc, i ,”ENDDA” , getDate(data) );
setTagValue(finaldoc, i ,”ENDTI” , getTime(data) );
data = “”;

data = getSourceTagValue(source, “PUW”);
setTagValue(finaldoc, i ,”PUW” , data );
data = “”;

setTagValue(finaldoc, i ,”UNIT” , “MWh” );

}

/**
* W przypadku braku komunikatu Response, mapujemy komunikat bledu
*/
/****/
if(l.getLength() == 0){
l = doc.getElementsByTagName(“faultstring”);
node = l.item(0);
if (node.getNodeType() == Node.ELEMENT_NODE) {
data = l.item(0).getChildNodes().item(0).getNodeValue();
finaldoc = buildOutputXML(l.getLength());
setTagValue(finaldoc, 0 , “KODKO”, data);
data = “”;
}
}
/***/

// Budujemy strukturę docelowa

TransformerFactory tf = TransformerFactory.newInstance();
Transformer transform = tf.newTransformer();
transform.setOutputProperty(OutputKeys.ENCODING, “UTF-8”);
transform.setOutputProperty(OutputKeys.METHOD, “xml”);
transform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, “yes”);
transform.setOutputProperty(OutputKeys.INDENT, “no”);
transform.transform(new DOMSource(finaldoc), new StreamResult(out));

} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
try {
Document finaldoc = buildOutputXML(1);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transform;
transform = tf.newTransformer();
transform.setOutputProperty(OutputKeys.ENCODING, “UTF-8”);
transform.setOutputProperty(OutputKeys.METHOD, “xml”);
transform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, “yes”);
transform.setOutputProperty(OutputKeys.INDENT, “no”);
transform.transform(new DOMSource(finaldoc), new StreamResult(out));
} catch (TransformerConfigurationException e1) {
e1.printStackTrace();
} catch (TransformerException e1) {
e1.printStackTrace();
} catch (ParserConfigurationException e1) {
e1.printStackTrace();
}
}
}

/***************************************************************************************/
/**
*
*/
public void setParameter(Map arg0) {
}
/***************************************************************************************/
/** Metoda ustawia wartosc VALUE w tagu TAG w wezle item o numerze ITEM_NUMBER
* liczac od gory komunikatu DOC
*
* @param doc
* @param item_number
* @param tag
* @param value
*/
public void setTagValue(Document doc, int item_number , String tag , String value){

Element root = doc.getDocumentElement();
NodeList nl = root.getElementsByTagName(tag);

for(int i = 0; i < nl.getLength(); i++){
if(i == item_number){
Node node = nl.item(i);

if(node instanceof Element) {
Element e = (Element) node;
e.appendChild(doc.createTextNode(value));
}
}
}
}
/***************************************************************************************/
/** Metoda pobierajaca wartosci poszczegolnych tagow komunikatu XML
*
* @param doc
* @param tag
* @return
*/
public String getSourceTagValue(Document doc, String tag){

String ret = “”;

try{
Element root = doc.getDocumentElement();
NodeList nl = root.getElementsByTagName(tag);

for(int i = 0; i < nl.getLength(); i++){
Node node = nl.item(i);
if(node instanceof Element){
Element e = (Element) node;
ret = e.getChildNodes().item(0).getNodeValue();
}
}

return ret;

} catch (NullPointerException e){
return ret = “”;
}
}
/***************************************************************************************/
/** Metoda zwraca substring parametru wejściowego bedacy godzina w formacie hh:mm
*
* @param input
* @return
*/
public String getDate(String input){

if(input != “”){
String output = input.substring(0,10);
return output;
}
else{
return “”;
}
}
/***************************************************************************************/
/** Metoda zwraca substring parametru wejsciowego będący datą w formacie YYYY-MM-DD
*
* @param time
* @return
*/
public String getTime(String input){

if(input != “”){
String output = input.substring(11,16);
return output;
}
else{
return “”;
}
}
/***************************************************************************************/
/** Metoda buduje docelową strukture komunikatu wysyłanego z SAP PI do SAP BW, ktora nie zawiera danych.
*
* @param quantity
* @return
* @throws ParserConfigurationException
*/
public Document buildOutputXML(int quantity) throws ParserConfigurationException{

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

// root elements
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement(“Response_MT”);
rootElement.setAttribute(“xmlns”, “http://gdfsuez.pl/bw/Events”);
doc.appendChild(rootElement);

// data
Element data = doc.createElement(“DATA”);
rootElement.appendChild(data);

for( int i = 0; i < quantity; i++){

// item
Element item = doc.createElement(“item”);
data.appendChild(item);

// kodko
Element kodko = doc.createElement(“KODKO”);
item.appendChild(kodko);

// timst
Element timst = doc.createElement(“TIMST”);
item.appendChild(timst);

// id
Element id = doc.createElement(“ID”);
item.appendChild(id);

// jdu
Element jdu = doc.createElement(“JDU”);
item.appendChild(jdu);

// blok
Element blok = doc.createElement(“BLOK”);
item.appendChild(blok);

// date0
Element date0 = doc.createElement(“DATE0”);
item.appendChild(date0);

// time
Element time = doc.createElement(“TIME”);
item.appendChild(time);

// zd
Element zd = doc.createElement(“ZD”);
item.appendChild(zd);

// tst
Element tst = doc.createElement(“TST”);
item.appendChild(tst);

//kwpr
Element kwpr = doc.createElement(“KWPR”);
item.appendChild(kwpr);

//tsw
Element tsw = doc.createElement(“TSW”);
item.appendChild(tsw);

// kwpo
Element kwpo = doc.createElement(“KWPO”);
item.appendChild(kwpo);

// zs
Element zs = doc.createElement(“ZS”);
item.appendChild(zs);

// pobc
Element pobc = doc.createElement(“POBC”);
item.appendChild(pobc);

// unit
Element unit = doc.createElement(“UNIT”);
item.appendChild(unit);

// tr
Element tr = doc.createElement(“TR”);
item.appendChild(tr);

// zdr
Element zdr = doc.createElement(“ZDR”);
item.appendChild(zdr);

// endda
Element endda = doc.createElement(“ENDDA”);
item.appendChild(endda);

// endti
Element endti = doc.createElement(“ENDTI”);
item.appendChild(endti);

// tu
Element tu = doc.createElement(“TU”);
item.appendChild(tu);

// puw
Element puw = doc.createElement(“PUW”);
item.appendChild(puw);
}
return doc;
}
}

About maciejbialek

Hello! I'm passionate, certified ABAP developer and SAP Consultant with many years of hands-on experience gained across several projects in Telco and Energy & Utilities industries. I've worked as Integration Architect, PI and ABAP Developer as well as BPC (NW) Consultant for local and international clients.

Leave a Reply