This blog aims in explaining how to assign ASMAs (adapter specific message attributes) for each child message of a BULK message using custom Dynamic Configuration (DC) header fields which can be set using a mapping program during design time, and which will be effective at runtime in IE/AE. To achieve this we require a custom generic adapter module, one ABAP FM and one ABAP table. We can also achieve this, by reading the required child payload fields information and then assigning them to ASMAs using a custom adapter module. However, I am focusing on reading custom DC headers method in adapter module as it has more advantages.
Steps:-
1. Set the custom DC headers with payload fields, Bulk Message ID and counter during design using multi-mapping. We can pass these values to the custom adapter module using module configuration in the communication channel.
2. Read these custom headers sequentially with the help of a custom adapter module & ABAP FM.
Flow Chart:-
Advantages:-
Sample Dynamic Configuration Code for mappings (UDF)
DynamicConfigurationKey keyA[] = new DynamicConfigurationKey[a.length];
result.addValue("1");
String allKeyValues = "";
for(int i = 0; i < a.length; i++)
{
try
{
DynamicConfiguration conf = (DynamicConfiguration) container.getTransformationParameters().get(StreamTransformationConstants.DYNAMIC_CONFIGURATION);
keyA[i] = DynamicConfigurationKey.create("urn:CustomDCHdr", "a" + Integer.toString(i+1) );
conf.put(keyA[i], a[i]);
keyA[i] = DynamicConfigurationKey.create("urn:CustomDCHdr", "b" + Integer.toString(i+1) );
conf.put(keyA[i], "/SCD/ich/pi/MULTIMAPPING");
if(i == a.length-1)
{
DynamicConfigurationKey keyCount = DynamicConfigurationKey.create("urn:CustomDCHdr", "Int1Count" );
conf.put(keyCount, Integer.toString(a.length) );
String messageID = (String) container.getTransformationParameters().get(StreamTransformationConstants.MESSAGE_ID);
DynamicConfigurationKey orgBulkMsgID = DynamicConfigurationKey.create("urn:CustomDCHdr", "BulkMsgID" );
conf.put(orgBulkMsgID, messageID);
}
}
catch (Exception e)
{
e.printStackTrace( );
}
}
Sample Bulk and Child Message Details
FM Definition in PI
DB Table Definition in PI
FM code
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(IV_BULKMSGID) TYPE STRING
*" VALUE(IV_SEQNO) TYPE INTEGER2
*" VALUE(IV_ACTION) TYPE STRING
*" VALUE(IV_FLAG) TYPE STRING OPTIONAL
*" EXPORTING
*" VALUE(EV_COUNT) TYPE INTEGER
*"----------------------------------------------------------------------
DATA: lv_BULKMSG TYPE STRING.
DATA: lv_SEQNO TYPE I.
DATA: lv_ACTION TYPE STRING.
DATA: lv_FLAG TYPE STRING.
DATA: lv_ZBULKMSGCOUNT TYPE I.
DATA: lv_COUNT TYPE I.
TABLES ZBULKMSGCOUNT.
lv_COUNT = 0.
SELECT SINGLE BULKMSGID SEQNO ACTION CHILDMSGCOUNT FROM ZBULKMSGCOUNT INTO (lv_BULKMSG, lv_SEQNO, lv_ACTION, lv_ZBULKMSGCOUNT) WHERE BULKMSGID = IV_BULKMSGID.
* SELECT SINGLE BULKMSGID FROM ZBULKMSGCOUNT INTO lv_BULKMSG WHERE BULKMSGID = IV_BULKMSGID.
IF sy-subrc EQ 0.
IF IV_FLAG = 'X' OR IV_FLAG = 'x'.
DELETE FROM ZBULKMSGCOUNT WHERE BULKMSGID = IV_BULKMSGID.
EV_COUNT = 0.
EXIT.
ENDIF.
IF lv_SEQNO NE IV_SEQNO.
IF lv_ACTION EQ IV_ACTION.
lv_COUNT = lv_ZBULKMSGCOUNT + ( IV_SEQNO - lv_SEQNO ).
EV_COUNT = lv_COUNT.
ELSE.
lv_COUNT = 1.
EV_COUNT = lv_COUNT.
ENDIF.
UPDATE ZBULKMSGCOUNT SET SEQNO = IV_SEQNO ACTION = IV_ACTION CHILDMSGCOUNT = lv_COUNT WHERE BULKMSGID = IV_BULKMSGID.
ENDIF.
IF lv_SEQNO EQ IV_SEQNO AND lv_ACTION EQ IV_ACTION.
EV_COUNT = lv_ZBULKMSGCOUNT.
ENDIF.
IF lv_SEQNO EQ IV_SEQNO AND lv_ACTION NE IV_ACTION.
lv_COUNT = 1.
EV_COUNT = lv_COUNT.
UPDATE ZBULKMSGCOUNT SET ACTION = IV_ACTION CHILDMSGCOUNT = lv_COUNT WHERE BULKMSGID = IV_BULKMSGID.
ENDIF.
ELSE.
ZBULKMSGCOUNT-BULKMSGID = IV_BULKMSGID.
ZBULKMSGCOUNT-SEQNO = IV_SEQNO.
ZBULKMSGCOUNT-ACTION = IV_ACTION.
ZBULKMSGCOUNT-CHILDMSGCOUNT = 1.
EV_COUNT = 1.
INSERT INTO ZBULKMSGCOUNT VALUES ZBULKMSGCOUNT.
ENDIF.
ENDFUNCTION.
Module Configuration in Communication Channel
Note: enable ASMA in the receiver file channel
*
7:24 PM 3/30/2011 Now updated the module code to get PI server information from module configuration. Now you to need provide encryppted (BASE64) password. Use this link base64 encoding (http://www.opinionatedgeek.com/dotnet/tools/Base64Encode/Default.aspx) to provide base64 encrypted password as below along with other PI server information. e.g., 100;pisuper;pwbase64xxxxxxx;EN;piserver.domain.com;00
Adapter Module Code
/* Author: Praveen Gujjeti */
package bulkmsg;
import com.sap.aii.adapter.xi.ms.*;
import com.sap.aii.af.mp.module.*;
import com.sap.aii.af.ra.ms.api.*;
import com.sap.aii.af.ra.ms.spi.*;
import com.sap.aii.af.service.auditlog.*;
import javax.ejb.*;
import java.util.StringTokenizer;
import com.sap.mw.jco.*;
public class ChildMsgSequenceNumberBean
implements SessionBean, Module
{
public ChildMsgSequenceNumberBean(){}
public void ejbRemove(){}
public void ejbActivate(){}
public void ejbPassivate(){}
public void setSessionContext(SessionContext context)
{
myContext = context;
}
public void ejbCreate()
throws CreateException{}
public ModuleData process(ModuleContext moduleContext, ModuleData inputModuleData) throws ModuleException
{
AuditMessageKey amk = null;
try
{
Object obj = null;
Message msg = null;
XIMessage xiMsg = null;
MessageProperties msgProp = null;
int childMsgSequenceNumber = 0;
String action = null;
String bulkMsgID = null;
String rfcPayload = null;
String logicalSequenceNumber = null;
String customHeaderNS = (String) moduleContext.getContextData("CustomNS"); //Custom DC NameSpace
String customHeaderTNS = (String) moduleContext.getContextData("CustomTNS"); //Custom DC Technical Names
String customeHeaderCount = (String) moduleContext.getContextData("Count"); //Custom DC Count
obj = inputModuleData.getPrincipalData();
msg = (Message) obj;
xiMsg = (XIMessage) obj;
if(msg.getMessageDirection() == MessageDirection.INBOUND)
amk = new AuditMessageKey(msg.getMessageId(), AuditDirection.INBOUND);
else
amk = new AuditMessageKey(msg.getMessageId(), AuditDirection.OUTBOUND);
msgProp = xiMsg.getMessageProperties();
childMsgSequenceNumber = msgProp.sequenceNumber; //AFW generate Sequence Number for Child message
action = msg.getAction().getName(); //Inbound Interface Name for Adapter
bulkMsgID = (String) msg.getMessageProperty(customHeaderNS, "BulkMsgID"); //Bulk Message ID from custom DC header
Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "BulkMessageID from custom DC header: " + bulkMsgID);
Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "Action(Inbound interface name): " + msg.getAction().getName());
Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "AFW generated child message sequence number: " + childMsgSequenceNumber);
Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "Total Custom DC Keys COUNT for the interface '" + msg.getAction().getName() + "': " + msg.getMessageProperty(customHeaderNS, customeHeaderCount));
// Enumeration ctxDataKeys = moduleContext.getContextDataKeys();
// while(ctxDataKeys.hasMoreElements())
// {
// String key = (String)ctxDataKeys.nextElement();
// Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "getContextDataKey: " + key);
// Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "getContextDataKey&Value: " + key + "=" + moduleContext.getContextData(key));
// }
//################ JCO Client Code for FM ZBULKMSGDETAILS which genarates logical Sequence Number #######################
JCO.Repository mRepository = null;
JCO.Client mConnection = JCO.createClient("<SAPCLIENTNUMBER>", "<USER>", "<PASSWORD>", "EN", "<PISERVERHOSTNAME>", "<SAPSYSTEMNUMBER>"); //Better provide this information from Module configuration. Password will be exposed.
//JCO.Client mConnection = JCO.createClient("100", "<USER>", "<PASSWORD>", "EN", System.getProperty("SAPDBHOST").toString(), System.getProperty("SAPSYSTEM").toString());
mConnection.connect();
mRepository = new JCO.Repository( "JCORepository", mConnection );
JCO.Function function = null;
IFunctionTemplate ft = mRepository.getFunctionTemplate("ZBULKMSGDETAILS");
function = ft.getFunction();
JCO.ParameterList input = function.getImportParameterList();
input.setValue(bulkMsgID, "IV_BULKMSGID");
input.setValue(new Integer(childMsgSequenceNumber), "IV_SEQNO");
input.setValue(action, "IV_ACTION");
input.setValue("", "IV_FLAG");
JCO.ParameterList output = function.getExportParameterList();
mConnection.execute( function );
logicalSequenceNumber = output.getValue("EV_COUNT").toString();
if ( ( msg.getMessageProperty(customHeaderNS, customeHeaderCount) ).equals(logicalSequenceNumber ) && ( moduleContext.getContextData("DEL").toString() ).compareToIgnoreCase("x") == 0)
{
input.setValue(bulkMsgID,"IV_BULKMSGID");
input.setValue("X","IV_FLAG");
input.setValue(new Integer(childMsgSequenceNumber),"IV_SEQNO");
input.setValue(action, "IV_ACTION");
mConnection.execute( function );
if ( (output.getValue("EV_COUNT").toString()).equals("0"))
{
Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "Bulk Msg ID '"+ bulkMsgID + "' record deleted from the SAP DB table 'ZBULKMSGCOUNT'");
}
}
mConnection.disconnect();
//################ JCO Client Code for FM ZBULKMSGDETAILS which genarates logical Sequence Number #######################
Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "Child message logical sequence Number generated by the adpater module: " + logicalSequenceNumber);
StringTokenizer st = new StringTokenizer(customHeaderTNS, ",");
String[] customHeaderTN = new String[st.countTokens()]; //Custom DC Technical Names array
//String[] ASMANameSpace = new String[st.countTokens()]; //Standard ASMA NameSpace array
//String[] ASMATechnicalName = new String[st.countTokens()]; //Standard ASMA Technical Name array
String[] payloadFieldForStandardDC = new String[st.countTokens()]; //payload field from DC header for Standard ASMA Technical Object
for (int j = 0; st.hasMoreTokens(); j++)
{
customHeaderTN[j] = st.nextToken();
payloadFieldForStandardDC[j] = (String) msg.getMessageProperty(customHeaderNS, customHeaderTN[j]+logicalSequenceNumber);
Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "Inserting ASMA Key: " + "(\"" + moduleContext.getContextData("key." + Integer.toString(j)) + "\"" + " " + "\"" + moduleContext.getContextData("value." + Integer.toString(j))+ "\")" + " with value: " + payloadFieldForStandardDC[j]);
//Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "Child message logical sequence Number generated by the adpater module: " + logicalSequenceNumber);
//msg.setMessageProperty("http://sap.com/xi/XI/System/File","FileName",payloadFieldForStandardDC);
try
{
msg.setMessageProperty((String) moduleContext.getContextData("key." + Integer.toString(j)), (String) moduleContext.getContextData("value." + Integer.toString(j)),payloadFieldForStandardDC[j]);
}
catch (InvalidParamException e)
{
throw new ModuleException("Unable to set Message Property", e);
}
}
}
catch(Exception e)
{
Audit.addAuditLogEntry(amk, AuditLogStatus.ERROR, e.toString());
ModuleException me = new ModuleException(e);
throw me;
}
return inputModuleData;
}
private SessionContext myContext;
}
Sample Audit Log of the adapter with custom module
Download the EAR file @ http://www.box.net/shared/hpdxrem5pf
I created an Idea to SAP to support this feature by default instead of custom development.
Please have a look at my idea and if you like please VOTE: https://cw.sdn.sap.com/cw/ideas/10218 (SAP PI should support dynamic configuration for 1:n multi-mapping scenarios)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
6 | |
6 | |
5 | |
4 | |
3 | |
3 | |
3 | |
3 | |
3 | |
2 |