cancel
Showing results for 
Search instead for 
Did you mean: 

Java mapping runtime issue

Former Member
0 Kudos

Hi,

I have currently created a java mapping which unzips files and transforms plain text/falt files to xml according to a specific structure... So far so good. This stuff works perfectly when testing interface mapping in the repository. However at runtime it does not work, and this is where you clever guys come into the picture:-)

It should be mentioned that the zip-file is dropped on a MQ-queue and picked up in XI and passed on to a BPM - the only thing the BPM does is to perform the java mapping and afterwards send one of the messages to R/3 and the other to CRM.

The concrete error given at runtime in the PE is:

Error handling for work item 000000718016

Work item 000000718016: Object CL_SWF_XI_MSG_BROKER method CALL_TRANSFORMATION cannot be executed

Parsing error before mapping: unexpected end-of-file (line 1, column 1)

Hope somone can help me on this matter,

Best regards,

Daniel

PS: In case it is of interest/use the java code used is this:

package dk.post.xi.unzipAndConvert;

import java.io.*;

import java.util.Map;

import java.util.HashMap;

import java.util.zip.ZipEntry;

import java.util.zip.ZipInputStream;

import com.sap.aii.mapping.api.MappingTrace;

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

import com.sap.aii.mapping.api.StreamTransformationConstants;

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

public class UnzipMain implements StreamTransformation {

private MappingTrace mappingTrace = null;

private Map param = null;

private StringBuffer currentXmlRecord = new StringBuffer();

private StringBuffer currentPlainRecord = new StringBuffer();

private int[][] dataRanges = null;

private char[] charsInCurrentRange = new char[650];

private String[] xmlTagNames = null;

/* (non-Javadoc)

  • @see com.sap.aii.mapping.api.StreamTransformation#setParameter(java.util.Map)

  • Method must be implemented when class is implementing streamTransformation

*/

public void setParameter(Map param) {

this.param = param;

if (param == null) {

this.param = new HashMap();

}

}

/*

  • (non-Javadoc)

  • @see com.sap.aii.mapping.api.StreamTransformation#execute(java.io.InputStream, java.io.OutputStream)

  • Main function -- called by XI to start execution of java mapping

*/

public final void execute(InputStream in, OutputStream out)

throws StreamTransformationException

{

//if no input, then cancel program execution

if (in == null) {

throw new RuntimeException("Something wrong with input zip file - is null");

}

//input <> null. Begin upzip operation

try {

//test is only relevant outside of XI

if (param != null) {

mappingTrace = (MappingTrace) param.get(StreamTransformationConstants.MAPPING_TRACE );

}

ZipInputStream zip = null;

try {

zip = new ZipInputStream(in);

ZipEntry ze = null;

out.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><ns0:Messages xmlns:ns0=\"http://sap.com/xi/XI/SplitAndMerge\">".getBytes());

while ((ze = zip.getNextEntry()) != null) {

writeDebugInfo("File: " + ze.getName() + " with compressed size: " + ze.getCompressedSize());

scrutinize(zip,(ZipEntry) ze, out);

}

out.write("</ns0:Messages>".getBytes());

}

finally {

if (zip != null)

zip.close();

out.flush();

out.close();

}

} catch (Exception e) {

throw new RuntimeException(e + "\nSomething went wrong...");

}

}

//Unzip flat files and convert to xml

private void scrutinize(ZipInputStream inStream,ZipEntry zipEntry, OutputStream outstream) throws Exception {

long start = System.currentTimeMillis();

InputStreamReader inStreamReader = null;

BufferedReader buffReader = null;

byte[] xmlBytes = null;

try {

inStreamReader = new InputStreamReader(inStream);

buffReader = new BufferedReader(inStreamReader);

boolean masterData = zipEntry.getName().equals("PD-01-STAMDATA.DAT");

boolean creditData = zipEntry.getName().equals("PD-02-KREDITVURDERING.DAT");

String currentLine = null;

if (masterData) {

writeDebugInfo("- Begin reading and converting every line of MasterData..."); //out-comment this outside of XI, or compilation will fail (functionality is only available in XI)

outstream.write("<ns0:Message1><ns1:masterData_MT xmlns:ns1=\"http://pdk/xi/kob/importDataToSap\">".getBytes());

while ((currentLine = buffReader.readLine()) != null) {

//convert one line of credit data plain into an xml record structure

makeXmlRecord(currentLine,true);

//Write record XML to XI's outputstream (appends)

xmlBytes = currentXmlRecord.toString().getBytes();

outstream.write(xmlBytes);

}

outstream.write("</ns1:masterData_MT></ns0:Message1>".getBytes());

writeDebugInfo("- Finished reading and converting every line of MasterData..."); //out-comment this outside of XI, or compilation will fail (functionality is only available in XI)

}

if (creditData) {

writeDebugInfo("- Begin reading and converting every line of CreditData...");

outstream.write("<ns0:Message2><ns1:creditData_MT xmlns:ns1=\"http://pdk/xi/kob/importDataToSap\">".getBytes());

while ((currentLine = buffReader.readLine()) != null) {

//convert one line of credit data plain into an xml record structure

makeXmlRecord(currentLine,false);

//Write record XML to XI's outputstream (appends)

xmlBytes = currentXmlRecord.toString().getBytes();

outstream.write(xmlBytes);

}

outstream.write("</ns1:creditData_MT></ns0:Message2>".getBytes());

writeDebugInfo("- Finished reading and converting every line of CreditData...");

}

} finally {

long timeUsed = System.currentTimeMillis() - start;

writeDebugInfo(zipEntry.getName() + " - Write took: " + timeUsed + " ms");

writeDebugInfo("Final statement entered...\n");

}

}

//check for invalid xml chars (plus a few other strange chars) in a plain text record and if any are found update the ranges of so they correspond to the new length of a record

private int[][] checkRange(String currentPlainLine, boolean isMasterData) {

int counter = 0;

int beginRange = 0;

int endRange = 0;

//populate array with proper data ranges/intervals according to how data is to be split

dataRanges = isMasterData ? new int[][] {{0,2},{2,12},{12,44},{44,47},{47,50},{50,295},{295,298},{298,356},{356,426},{426,496},{496,499},{499,506},{506,510},{510,518},{518,540},{540,546},{546,552},{552,558},{558,564},{564,570},{570,576},{576,582},{582,588},{588,594},{594,604}} : new int[][] {{0,2},{2,12},{12,20},{20,23},{23,26},{26,29},{29,44}};

//ensure stringbuffer is empty

currentPlainRecord.delete(0,currentPlainRecord.length());

//insert the current plain text line into stringbuffer

currentPlainRecord.append(currentPlainLine);

//loop through outer array (array of ranges)

for (int i = 0; i <= (dataRanges.length-1); i++) {

beginRange = dataRanges<i>[0];

counter = beginRange;

endRange = dataRanges<i>[1];

//loop through stringbuffer, based on the value-pairs of ranges/intervals in array of arrays

while (counter < endRange) {

switch (currentPlainRecord.charAt(counter)) {

case '&':

currentPlainRecord.insert(counter+1,"amp;"); //insert 'amp;' after '&', so that syntax conforms to xml syntax requirements (&amp;)

counter += 5; //increment counter so that it continues after insertet characters

updateRange(counter,4); //update the array of ranges so that is corresponds to the new range

break;

case '\'':

currentPlainRecord.replace(counter,counter+1,"&"); //replace '\'' with '&'

currentPlainRecord.insert(counter+1,"apos;"); //insert 'apos;' after '\'', so that syntax conforms to xml syntax requirements (&apos;)

counter += 6; //increment counter so that it continues after insertet characters

updateRange(counter,5); //update the array of ranges so that is corresponds to the new range

break;

case '"':

currentPlainRecord.replace(counter,counter+1,"&"); //replace '"' with '&'

currentPlainRecord.insert(counter+1,"quot;"); //insert 'quot;' after '"', so that syntax conforms to xml syntax requirements (&quot;)

counter += 6; //increment counter so that it continues after insertet characters

updateRange(counter,5); //update the array of ranges so that is corresponds to the new range

break;

case '<':

currentPlainRecord.replace(counter,counter+1,"&"); //replace '<' with '&'

currentPlainRecord.insert(counter+1,"lt;"); //insert 'lt;' after '&', so that syntax conforms to xml syntax requirements (&lt;)

counter += 4; //increment counter so that it continues after insertet characters

updateRange(counter,3); //update the array of ranges so that is corresponds to the new range

break;

case '>':

currentPlainRecord.replace(counter,counter+1,"&"); //replace '>' with '&'

currentPlainRecord.insert(counter+1,"gt;"); //insert 'gt;' after '&', so that syntax conforms to xml syntax requirements (&gt;)

counter += 4; //increment counter so that it continues after insertet characters

updateRange(counter,3); //update the array of ranges so that is corresponds to the new range

break;

case 0x7F:

currentPlainRecord.replace(counter,counter+1," "); //replace 0x7F with ' '

writeDebugInfo("-- Corrected the character 0x7F ('') to ' '");

counter++;

break;

case '‘':

currentPlainRecord.replace(counter,counter+1," "); //replace 0x91 ('‘') with ' '

writeDebugInfo("-- Corrected the character '‘' to ' '");

counter++;

break;

case '›':

currentPlainRecord.replace(counter,counter+1," "); //replace 0x9B ('›') with ' '

writeDebugInfo("-- Corrected the character 0x9B to ' '"); counter++;

break;

case '†':

currentPlainRecord.replace(counter,counter+1," "); //replace '†' with ' '

writeDebugInfo("-- Corrected the character '†' to ' '");

counter++;

break;

default :

counter++;

break;

}

}

}

return dataRanges;

}

//update array of ranges so that is corresponds to the new range

private void updateRange(int currentRange, int updateRangeBy) {

for (int k = currentRange; k < dataRanges.length; k++) {

if (k != currentRange) { //never update begin interval for the current range - only for the remaining intervals

dataRanges[k][0] += updateRangeBy;

}

dataRanges[k][1] += updateRangeBy; //always update end intervals

}

}

//convert one flat record line to a xml record structure

private void makeXmlRecord(String currentPlainLine,boolean isMasterData) {

dataRanges = checkRange(currentPlainLine,isMasterData);

//clear StringBuffer

currentXmlRecord.delete(0,currentXmlRecord.length());

//get the correct set of xml tags to be used when performing mapping between plain text and xml

if (isMasterData) {

xmlTagNames = new String[] {"dum1","kob_no","dum2","corp_status","legal_form","dum3","pr_protec_code","dum4","uri","email","emp_no_grp_code","exact_no_emp","dum5","founding_date","dum6","main_industry_code","industry_code1","industry_code2","industry_code3","industry_code4","industry_code5","industry_code6","industry_code7","industry_code8","dum7"};

} else {

xmlTagNames = new String[] {"dum1","kob_no","rating_date","rating_value","risc_grp_code","currency","max_credit"};

}

//copy all chars in stringBuffer to the specified char[]

currentPlainRecord.getChars(0,currentPlainRecord.length(),charsInCurrentRange,0);

//populate XML record structure

currentXmlRecord.append("<record>");

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

currentXmlRecord.append("<" + xmlTagNames<i> + ">");

currentXmlRecord.append(charsInCurrentRange,dataRanges<i>[0],(dataRanges<i>[1])-(dataRanges<i>[0]));

currentXmlRecord.append("</" + xmlTagNames<i> + ">");

}

currentXmlRecord.append("</record>");

}

//write debug information to correct "place"

private void writeDebugInfo(String debugInfo) {

if (mappingTrace == null) {

System.out.println("Debug: " + debugInfo);

} else {

mappingTrace.addInfo("Debug: " + debugInfo);

}

}

}

Accepted Solutions (0)

Answers (1)

Answers (1)

former_member184154
Active Contributor
0 Kudos

Hi Daniel,

of course this is just so straaange...

Sounds like BPM is expecting some XML stuff as transformation input...

A couple of advices:

1 - if you are on a certain SP level (say 14, but I'm not sure, check it out), you can easily dismantle the BPM and have split interface mapping do this job, that is sending two different messages

2 - figure out a way to switch from that horrible xml String-based bulding mode to some more reliable and canonical way, like DOM or SAX (believe me)

3 - finally, something that could really help you out: I have done something similar, and found very useful putting my raw input content (flat file in my case, zip/binary stuff in yours) in a dummy XML doc as a big CDATA section. I've done this via custom module set in the sender CommChannel. One option is to use a generic mapping module I wrote (/people/alessandro.guarneri/blog/2006/03/16/xi-mapping-module-for-afw). In this way you'll have a 100% XML doc coming in your transformation (so also BPM could be working) and you'll just have to additionally pick up the zip file from the value of the CDATA section.

Alex

Former Member
0 Kudos

Hi Alex,

Thanks for your reply. First of all I was wondering if I missed something somewhere in my generel understanding of XI and BPM's!? - I kind of sense some irony/sarkasm in beginning of your reply:-) As far as I know BPM should have no trouble receing non-xml (in my case zip-files)!?

Anyways here goes:

1. Yes, this is also my impression that is should be possible somehow (we do currently run sp14). However, I havent yet figured out how to do it in this case. When trying to create interface mapping between the three involved software components, I get a 'MESS_MULTI_MAP_IF_WRONG_SWCV' three times. I've searched around without being able to find any information on the error. I does seem, though, like the problem is that all objects aren't in the same software component!! But this approach is of course the prefered one...

2. Well - you got me there. Kind of expected someone to point that out:-) This is of course something to be looked into also in order to "improve"/"enhance" the program - though it, as already pointed out, works perfectly as is.

3. This part I have also thought about - though quickly moved away from the idea, but that is only due to lacking insight/knowhow on the subject. Interesting weblog you have written, something so I will investigate further when time allows for it:-)

Best regards,

Daniel