Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
roger_alluivall
Participant
17,510

Adapter Module: ReplaceString


Use

ReplaceString module is used to replace strings within the xml text of the payload of the message. It can be used to modify the message structure before it gets in PI and also before it gets out. It is useful when there are strings like namespaces, types, etc that PI can’t “understand” or that PI is unable to include in outbound messages.

The module obtains a pair of string : <OldString>;<NewString> as parameter. Reads the payload of the message and converts it to a string. Then it searches <OldString> within the message and replace each occurrence with <NewString>. Finally it returns the message modified. If OldString can’t be found in the message nothing is modified and the original message is returned.

The module accepts multiple string pairs to allow multiple replacements in a single execution.

Deployment

Enterprise Java Bean Project: ReplaceString-ejb

Enterprise Java Bean Application: ReplaceString-ear

Integration

The module can be used in any kind of Sender and Receiver adapters.

Activities

This section describes all the activities that have to be carried out in order to configure the module.

Entries in processing sequence

Insert the module in front of the adapter module as shown in the picture below.

Entries in the module configuration

The table below shows the possible parameters and values of the adapter module.

ParameterTypePossible valuesDescriptionExample
separatorOptional

Any alphanumeric character.

Default value: ‘|’

This parameter configures the character that separates a pair of strings.

separator = ‘;’
param(*)

Mandatory

Pair of strings separated by “separator” character.

<OldString><separator>[<NewString>, blankString,emptyString].

Default value: none.

This parameter is used to obtain Old string to be replaced by the New string in the message. The pair is separated by “separator”. As Adapter module tab in PI trims strings by the right side, if it’s needed to replace OldString by a blank string or an empty string, the parameters blankString or emptyString have to be used.

param1=str1;str2

param2=str1;blankString

param3=str1;emptyString

(*) The parameter name can be any string: param1, parameter, aaa, etc.

Example

Payload data before execution:


<MT_ROOT>
     <RECORDSET>
          <DATA>
               <FIELD1>value</FIELD1>
               <FIELD2>value</FIELD2>
               <FIELD3>value</FIELD3>
          </DATA>
          <DATA>
               <FIELD1>value</FIELD1>
               <FIELD2>value</FIELD2>
               <FIELD3>value</FIELD3>
          </DATA>
     </RECORDSET>
</MT_ROOT>




Module parameters:

separator = ‘;’

param1 = ’RECORDSET;ROW’

param2 = <MT_ROOT>;<MESSAGE type=TYPE>

param3 = </MT_ROOT>;</MESSAGE>

param4 = <DATA>;emptyString

param5 = </DATA>;emptyString

Payload data after execution:


< MESSAGE type=TYPE>
     <ROW>
          <FIELD1>value</FIELD1>
          <FIELD2>value</FIELD2>
          <FIELD3>value</FIELD3>
          <FIELD1>value</FIELD1>
          <FIELD2>value</FIELD2>
          <FIELD3>value</FIELD3>
     </ROW>
</MESSAGE>







ReplaceStringBean


/**
*
*/
package com.arms.integrations;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import com.sap.aii.af.lib.mp.module.Module;
import com.sap.aii.af.lib.mp.module.ModuleContext;
import com.sap.aii.af.lib.mp.module.ModuleData;
import com.sap.aii.af.lib.mp.module.ModuleException;
import com.sap.engine.interfaces.messaging.api.Message;
import com.sap.aii.af.service.auditlog.Audit;
import com.sap.engine.interfaces.messaging.api.MessageKey;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus;
import java.io.*;
import java.util.*;
/**
* @author Roger Allué i Vall
*
*/
public class ReplaceStringBean implements SessionBean, Module {
    private SessionContext myContext;
    private static final String C_SEPARATOR_STRING = "separator";
    private static final String C_AUX_SEPARATOR = "|";
    private static final String C_MODULEKEY_STRING = "module.key";
    private static final String C_BLANK_STRING = "blankString";
    private static final String C_EMPTY_STRING = "emptyString";
    /* (non-Javadoc)
     * @see javax.ejb.SessionBean#ejbActivate()
     */
    public void ejbActivate() throws EJBException, RemoteException {
        // TODO Auto-generated method stub
    }
    /* (non-Javadoc)
     * @see javax.ejb.SessionBean#ejbPassivate()
     */
    public void ejbPassivate() throws EJBException, RemoteException {
        // TODO Auto-generated method stub
    }
    /* (non-Javadoc)
     * @see javax.ejb.SessionBean#ejbRemove()
     */
    public void ejbRemove() throws EJBException, RemoteException {
        // TODO Auto-generated method stub
    }
    /* (non-Javadoc)
     * @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext)
     */
    public void setSessionContext(SessionContext arg0) throws EJBException,
            RemoteException {
        // TODO Auto-generated method stub
        myContext = arg0;
    }
    /* (non-Javadoc)
     * @see javax.ejb.SessionSynchronization#afterBegin()
     */
    public void afterBegin() throws EJBException, RemoteException {
        // TODO Auto-generated method stub
    }
    /* (non-Javadoc)
     * @see javax.ejb.SessionSynchronization#afterCompletion(boolean)
     */
    public void afterCompletion(boolean arg0) throws EJBException,
            RemoteException {
        // TODO Auto-generated method stub
    }
    /* (non-Javadoc)
     * @see javax.ejb.SessionSynchronization#beforeCompletion()
     */
    public void beforeCompletion() throws EJBException, RemoteException {
        // TODO Auto-generated method stub
    }
    public void ejbCreate() throws javax.ejb.CreateException {
    }
    public ModuleData process(ModuleContext moduleContext, ModuleData inputModuleData)    throws ModuleException {
        ByteArrayOutputStream payloadOut = null;
        int inputByte = 0;
        String payloadStr = "";
        Message msg;
        Enumeration paramList;
        String sep, paramKey,param, strArray[];
        try {
             msg = (Message) inputModuleData.getPrincipalData();
             MessageKey amk = new MessageKey(msg.getMessageId(), msg.getMessageDirection());
    
             Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,".-Module beginning");
payloadStr = new String(msg.getDocument().getContent(),"UTF-8");
    
            Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Payload before execution:" );
            Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,payloadStr );
             sep = moduleContext.getContextData(C_SEPARATOR_STRING);
             if ( sep == null ) {
                sep = C_AUX_SEPARATOR;
                Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Default separator used: " + sep);
             }
             else {
                Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Separator found: " + sep);
             }
    
   
    
             paramList = moduleContext.getContextDataKeys();
               while( paramList.hasMoreElements()) {
                    paramKey = (String) paramList.nextElement();
                 //Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"ParamKey: " + paramKey);
                 param =  moduleContext.getContextData(paramKey);
                 //Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Param: " + param);
        
                 if ( ! paramKey.equals(C_SEPARATOR_STRING) && ! paramKey.equals(C_MODULEKEY_STRING) ){
                    Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"ParamKey: " + paramKey);
                    Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Parameter: " + param);
                    Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Separator: " + sep);
           
                    strArray = param.split(sep);
                    if (strArray != null){
                       if ((! strArray[0].equals(null)) && (! strArray[1].equals(null))){
                        if ( strArray[1].equals(C_BLANK_STRING)){
                            Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Blank String");
                            strArray[1]=" ";                   
                        }
                        else if (strArray[1].equals(C_EMPTY_STRING)){
                            strArray[1]="";
                            Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Empty String");
                        }
                        Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Substitution strArray[0]: " + strArray[0]);
                        Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Substitution strArray[1]: " + strArray[1]);
                        Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Substitution payloadStrA : " + payloadStr);   
                        payloadStr = payloadStr.replaceAll(strArray[0],strArray[1]);
                        Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Substitution payloadStrB : " + payloadStr);
                       }
                    }
                 }
             }
             Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,"Payload after replacement:" );
             Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS,payloadStr );
   
             payloadOut = new ByteArrayOutputStream();
             payloadOut.write(payloadStr.getBytes());
             msg.getDocument().setContent(payloadOut.toByteArray());
             inputModuleData.setPrincipalData(msg);
        }
        catch(Exception e) {
            ModuleException me = new ModuleException(e);
            throw me;
        }
        return inputModuleData;
    }
}







31 Comments
former_member188961
Participant
0 Kudos

Hi Roger,

Thanks for sharing this Generic adapter module solution for replacing the string.. :smile:

Thanks,

Srikanth

Former Member
0 Kudos

Hi Roger,

:grin: I really like your adapter module series.

Thanks

VB

roger_alluivall
Participant
0 Kudos

Thank you Vladimir!

somil_gulati
Explorer
0 Kudos

Hi Roger,

The blog helped me a lot in developing a module.

Thanks

Somil.

roger_alluivall
Participant
0 Kudos

Hi Somil,

I'm very pleased you found it useful.

Thanks and Regards!

roger_alluivall
Participant
0 Kudos

Hello Aaron,

It seems something's wrong with your ear.  Could you please tell me how are you deploying you ear?  You can check if the module is correctly deployed from the perspective Deployment of NWDS. On left panel you should see that the module is running (in green).

Regards !

roger_alluivall
Participant
0 Kudos

Take a look to this document of William Li. It has an step by step of how to develop and deploy an adapter module:

http://scn.sap.com/docs/DOC-3816


Regards

Former Member
0 Kudos

Hi, I think the Ear is deployed OK now. However in RWB com channel monitor I get error: "Message processing failed. Cause: java.lang.ArrayIndexOutOfBoundsException: 1"

Does this perhaps mean my module parameters are maybe wrong? I tried with single quotes around the replace strings also. Is there a way to set breakpoint in NWDS or simulate the execution at runtime?

12.02.2015 17:31:35.788InformationMP: entering1
12.02.2015 17:31:35.790Information.-Module beginning
12.02.2015 17:31:35.790InformationMP: processing local module localejbs/ReplaceString
12.02.2015 17:31:36.017Information<ns0:MyMessage> ... [contents omitted] ... </ns0:MyMessage>
12.02.2015 17:31:36.017InformationParamKey: param1
12.02.2015 17:31:36.017InformationParameter: ns0;foobar
12.02.2015 17:31:36.017InformationPayload before execution:
12.02.2015 17:31:36.017InformationSeparator found: ;
12.02.2015 17:31:36.017InformationSeparator: ;
12.02.2015 17:31:36.018ErrorMP: exception caught with cause java.lang.ArrayIndexOutOfBoundsException: 1
12.02.2015 17:31:36.019ErrorAdapter Framework caught exception: 1
12.02.2015 17:31:36.020ErrorDelivering the message to the application using connection File_http://sap.com/xi/XI/System failed, due to: com.sap.engine.interfaces.messaging.api.exception.MessagingException: java.lang.ArrayIndexOutOfBoundsException: 1.
12.02.2015 17:31:36.025InformationThe asynchronous message was successfully scheduled to be delivered at Thu Feb 12 17:36:36 CET 2015.
12.02.2015 17:31:36.025InformationThe message status was set to WAIT.
roger_alluivall
Participant
0 Kudos

Hello Aaron,

Please, check the following pdf I created with all the screen shots of the whole process (creation/configuration, deployment, execution).

https://dl.dropboxusercontent.com/u/25955636/SAP%20PI%20Module.pdf

Let me know if you find it useful.

Regards!

Former Member
0 Kudos

Think I found the issue here is with the separator. If I use ';' in the config I get ArrayIndexOutOfBounds, but if I do not quote it in config it sends the message through the module OK.

Former Member
0 Kudos

I had only one other issue but solved it myself.

On lines 137, 138 Java replaceAll was not making the substitution until I converted the String Arrays to regular Strings using .toString() method. After that with the same parameters it started to work. This could maybe be down to our old java version? I saw from your document you use the latest and greatest.

Thanks so much Roger for posting this document and giving me some hints!!! I now have a basic understanding of how to write and deploy adapter modules.

Former Member
0 Kudos

Hi Roger

I am doing a REST --- PI --- ECC synchronous scenario. The xml payload is converted to JSON by REST channel and this causes an unwanted backward slash to be added to the payload.

like this

http://host:port/RESTAdapter/xyz

becomes

http:\/\/host:port\/RESTAdapter\/xyz


is there any module that can remove the backward slash from the target payload


Regards


Midhun

roger_alluivall
Participant
0 Kudos

Hello Midhun,


Sorry, but I didn't use the REST Adapter yet. However, If the URL is added to the payload you can always use my module.


Just to understand a bit more the problem, could you please paste an example of your payload?



Regards!

Former Member
0 Kudos

Hi Roger

This is the payload:

{

   "MT_EmployeeNumber_CRN_Response_Receiver": {

     "EmployeeNumberTable": [

       {

         "EmployeeNumber": "134541",

         "EmployeeName": Mark,

         "EmployeeDescription": "TEST 123",

         "EmployeeGroup": "SAP",

         "links": {

           "self": {

             "href":

"http:\/\/host:port\/RESTAdapter\/CRN\/EmployeeNumber\/134541"

           }

         }

       },

     ]

   }

}

This payload is converted from xml to JSON. When the conversion is done an additional backward slash is added in front of all the forward slashes in the url.

This is a synchronous scenarios, REST adapter at sender side and SOAP adapter at receiver side.

My requirement is to take the backward slash from the JSON Payload

Midhun

roger_alluivall
Participant
0 Kudos

Hello Midhun,

My module ReplaceString treats the payload as text, so it shouldn't distinguish between xml and json messages. Notice that the module uses the java method replaceAll. This method expects a regular expression and you should escape backslashes.

Could you configure the module with the following parameters?

param1=\\/;/

separator=;

Regards!

roger_alluivall
Participant
0 Kudos

Look, I have created a simple class to test replaceAll method:


import java.util.Scanner;



public class Test {


   


   


    public static void main(String[] args) {


       


       


        Scanner keyboard = new Scanner(System.in);


       


        System.out.println("Enter String:");


        String str = keyboard.nextLine();


        System.out.println("Enter Str1:");


        String str1 = keyboard.nextLine();


        System.out.println("Enter Str2:");


        String str2 = keyboard.nextLine();


       


        String str3 = str.replaceAll(str1, str2);


       


        System.out.println("Str: " + str);


        System.out.println("Str1: " + str1);


        System.out.println("Str2: " + str2);


        System.out.println("Str3: " + str3);


       


    }



}


And this is the result with your original string and the parameters I mentioned above:


Enter String:


http:\/\/host:port\/RESTAdapter\/xyz


Enter Str1:


\\/


Enter Str2:


/


Str: http:\/\/host:port\/RESTAdapter\/xyz


Str1: \\/


Str2: /


Str3: http://host:port/RESTAdapter/xyz


Regards!

Former Member
0 Kudos

Thanks a lot for your time, Roger

I am working on it and will update you. Your help was priceless and really appreciate the time you have taken to help me

roger_alluivall
Participant
0 Kudos

You are welcome!

Let me know if you managed. :wink:

stefan_grube
Active Contributor

Hi Roger,

Thank you for sharing your module. I think this will be helpful in many scenarios.

However, I have some doubts about following piece of code:

   while((inputByte = payloadIn.read()) != -1) { 

           payloadStr = payloadStr + (char) inputByte; 

   } 


As payloadIn.read() provides a byte, and you change it to (char), this leads to errors for all multi-byte UTF-8 character. For example the German character 'ä' (hex c3 a4) would be transformed to 'ä'


I recommend using following code line:


     payloadStr = new String(msg.getDocument().getContent(),"UTF-8");


This is the proper way to tranform a byte array into a String.


Regards

Stefan

roger_alluivall
Participant
0 Kudos

Thank you Stefan!!

I will update the code right now.

Regards!

roger_alluivall
Participant
0 Kudos

Hello Apu,

there are plenty of blogs on how to create your own module. Don't get me wrong, but there's a difference between sharing knowledge and working for free.

Good luck.

surender_durgam
Explorer
0 Kudos
Hi,

I have a problem with conversion on REST Receiver adapter, method GET. The communication channel is not converting the json payload to html, so the payload that returns to S4Hana is empty.

The interface is sycronous: SAP S4Hana (Proxy) -> SAP PO (REST) -> External System (GET method).

Here is the json returned from external system.

{“Details”:{“OrderDetails”:[{“OrderID”:”0″,”OrderNo”:”00010″ }],”CustomerID”:”00001″,”UserID”:””,”Name”:””,”PCode”:””,”Status”:”Sucess”,”Message”:”Record Inserted Successfully”}}

Here is the error message from PO system.

MP: exception caught with cause com.sap.aii.adapter.rest.ejb.parse.InvalidJSonContent: Invalid JSON message content used; Message: “JSONArray text must end with ] at character 28 of 

Bellow are the images.

Channel



 

Error Message



Please suggest me to resolve this error.

 

Regards

Surender

 
0 Kudos
Hello everybody

I'm trying to implement the Adapter Module ReplaceString for PI 7.4. However

the class com.sap.aii.af.service.auditlog.Audit cannot be imported as it does not

exists anymore. I didn't find the JAR-File anymore on the OS of the PI.

How to proceed in order to be able to use the Audit class?

Thanks for your feedback.

Kind regards

Chris

 
roger_alluivall
Participant
0 Kudos
Hello Chris,

 

The audit statements are just to trace the module execution and see what is doing in the channel monitoring log. If you want you can ommit them.

Regards!
0 Kudos
Hello Roger

thanks for your feedback.

I've found now a way to access the audit classes:
In https://blogs.sap.com/2016/04/16/make-your-custom-adapter-module-a-little-more-flexible/
the classes com.sap.engine.interfaces.messaging.api.auditlog.AuditAccess and
com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus are used
to define the audit variable. So I didn't have to comment the audit statements (because
they are still important for the trace messages to me).

I've deployed then the ReplaceString EAR according to the following link:
https://blogs.sap.com/2015/01/29/create-sap-pi-adapter-modules-in-ejb-30-standard/

After the deployment I can see 4 objects in the JNDI browser (like in
https://blogs.sap.com/2017/05/29/adapter-module-development-in-nwdi/ )

Unfortunately the adapter module ReplaceString does not seem to get called
(I've inserted ReplaceString before the CallSapAdapter, like shown in this blog).

In the message protocoll I see only:
"MP: processeing local module localejbs/CallSapAdapter" but I cannot find
"messages like ".-Module beginning" .

Somehow it seems that the module is not being called even though I've could successully
deploy it and it can be seen in the JNDI-Browser.

Do you have any clue how I can remedy to this problem?

Thanks for your feedback.

Kind regards
Christof
0 Kudos
PS: the current views in the EJB explorer and the JNDI explorer:



0 Kudos
PS2: I've found a difference in the EJB explorer between ReplaceStringBean and PlainConverterModule: the status is different for "com.sap.aii.af.lib.mp.module.Module:





=> could this be the reason that ReplaceString module is not being called? How to remedy?

 
0 Kudos
PS3: when I execute the test function in the EJB explorer I get the following error message for ReplaceString:

.........

Caused by: javax.ejb.EJBTransactionRolledbackException: ASJ.ejb.005044 (Failed in component: sap.com/ReplaceStr_EAR)

Exception raised from invocation of public com.sap.aii.af.lib.mp.module.ModuleData com.sap.pi.ReplaceString.process(

com.sap.aii.af.lib.mp.module.ModuleContext,com.sap.aii.af.lib.mp.module.ModuleData)

throws com.sap.aii.af.lib.mp.module.ModuleException method on bean instance com.sap.pi.ReplaceString@52332ad1

for bean sap.com/ReplaceStr_EAR*annotation|ReplaceStr_EJB.jar*xml|ReplaceStringBean in application

sap.com/ReplaceStr_EAR.; nested exception is: java.lang.NullPointerException: while trying to invoke

the method com.sap.aii.af.lib.mp.module.ModuleData.getPrincipalData() of a null object loaded from

local variable 'inputModuleData'; nested exception is: javax.ejb.EJBException: ASJ.ejb.005044

(Failed in component: sap.com/ReplaceStr_EAR) Exception raised from invocation

of public com.sap.aii.af.lib.mp.module.ModuleData com.sap.pi.ReplaceString.process(com.sap.aii.af.lib.mp.module.ModuleContext,

com.sap.aii.af.lib.mp.module.ModuleData) throws com.sap.aii.af.lib.mp.module.ModuleException method on bean

instance com.sap.pi.ReplaceString@52332ad1 for bean sap.com/ReplaceStr_EAR*annotation|ReplaceStr_EJB.jar*xml|

ReplaceStringBean in application sap.com/ReplaceStr_EAR.; nested exception is: java.lang.NullPointerException:

while trying to invoke the method com.sap.aii.af.lib.mp.module.ModuleData.getPrincipalData() of a null

object loaded from local variable 'inputModuleData'

.......

 

However the inputModuleData variable is instanciated like in your code above:

....

@Override
public ModuleData process(ModuleContext moduleContext,
ModuleData inputModuleData) throws ModuleException {

Message message = (Message) inputModuleData.getPrincipalData();
MessageKey messageKey = message.getMessageKey();

.......

 

Any clue on how to remedy on that?

 
0 Kudos
 

Hello Roger

the problem is solved: everythin was correct with the EJB Explorer.
I didn't properly call the ReplaceString Module in the sender adapter, thats
why it didn't get called. Now it's working.

However I have a question regarding your statement above:

"
while((inputByte = payloadIn.read()) != –1) {
payloadStr = payloadStr + (char) inputByte;
}
"

You replaced it by
"
payloadStr = new String(msg.getDocument().getContent(),”UTF-8″);
"

However this statement uses the SAXParser and it throws an expection when e.g.
I want to get rid of a @ (by replacing @ by emptystring) in REST response message
where the JSON-Structure is converted to an XML structure.
So in this case it would be better to have your piece of code (no SAXParser involved)
than the suggested statement by Stefan Grube (knowing that in this particular case
the problem with the Umlaute ä can arise).

Could you just share how in your example with
"
while((inputByte = payloadIn.read()) != –1) {
payloadStr = payloadStr + (char) inputByte;
}
"
you did define and initialize payloadIn (with class Inputstream or other)?

Thanks for your feedback.

Kind regards
Chris
SushantShinde
Advisor
Advisor
0 Kudos
Hello Roger,

Did you test your module with the REST adapter, Can you please share your experience.

Even I am facing issues handling backslash in xml to JSON conversion.

So would like to hear from you.

Regards,

Sushant
Akhila_A
Explorer
0 Kudos

Hi Roger Allue ,

 

Curious to know if this module helps to remove white spaces in the XML tags .

Source XML tags look like below where you see a space between Exchange and data, Value and data, We are not able to create a datatype since it has a space.

<Exchange data >12344</Exchange data>

<Value data>xyz<Value data>

 

 

Labels in this area