Skip to Content
Personal Insights
Author's profile photo Dilip Kumar Krishnadev Pandey

Custom Adapter Module in SAP PI

Overview

In a business scenario, a group of users send attachments to one mail-id. They can send multiple attachments per mail. So, here, we need to find out a way to extract all attachments with their original names from a mail and store it in some folder.

Using Sender-Mail adapter, we read mails with attachments.

For storing attachments to specific sap application directory, we use Receiver-File Adapter along with a custom java adapter module.

Note: Receiver-File-Adapter can store only 1st attachment, so, we create a custom adapter module for this channel which will store multiple attachments to folder.

Sender/Receiver Communication channels can be referred from below link:

 

Pre-requisites:

Pre-requisites to create a custom java adapter module form SAP PI 7.11 is as below:

  • SAP NetWeaver Developer Studio 7.1
  • Java Runtime Environment 1.6 (Jre 1.6)
  • SAP library (or jar) files for required for module development
    • sap.aii.af.lib.mod.jar                  : <bin>/ext/com.sap.aii.af.lib/lib
    • com~tc~logging~java~impl.jar  : <bin>/system
    • sap.aii.af.svc_api.jar                 : <bin>/services/com.sap.aii.af.svc/lib
    • sap.aii.af.cpa.svc_api.jar          : <bin>/services/com.sap.aii.af.cpa.svc/lib
    • sap.aii.af.ms.ifc_api.ja r            : <bin>/interfaces/com.sap.aii.af.ms.ifc/lib
  • Include above jar files in the library classpath of SAP Netweaver Developer Studio 7.1 via below steps:
    • Navigate to Windows → Preference → expand “Java” → select “Build Path” → select “Classpath Variables” → Click “New” to create a “New Variable Entry” → input “Name” as “PI_AF_LIBS” → select “Path” (above jar location)
  • NwceTool
    • This will help to convert EAR files to SDA files
    • It uses JRE 1.6

 

Java Adapter Module Code:

  • Below java adapter module code is been written to get SAP-Directory path with external module input variable ‘SapDirPath’ and save the atatchments with original fileName into the path:
    /**
     * 
     */
    package com.sap.adaptermodule;
    
    import java.rmi.RemoteException;
    import javax.ejb.EJBException;
    import javax.ejb.SessionBean;
    import javax.ejb.SessionContext;
    import javax.ejb.TimedObject;
    import javax.ejb.Timer;
    
    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.aii.af.service.auditlog.Audit;
    import com.sap.engine.interfaces.messaging.api.Message;
    import com.sap.engine.interfaces.messaging.api.MessageKey;
    import com.sap.engine.interfaces.messaging.api.Payload;
    import com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.util.Iterator;
    
    /**
     * @author User
     *
     */
    public class MAttachToDirBean implements SessionBean, TimedObject {
    
    	/* (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
    
    	}
    
    	/* (non-Javadoc)
    	 * @see javax.ejb.TimedObject#ejbTimeout(javax.ejb.Timer)
    	 */
    	public void ejbTimeout(Timer arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	public void ejbCreate() throws javax.ejb.CreateException {
    		
    	}
    	
    	private SessionContext myContext;
    	MessageKey amk = null;		
    	public ModuleData process(ModuleContext moduleContext, ModuleData inputModuleData) throws ModuleException
    	{
    		Object obj = null;
    	    Message msg = null;
    	    String SapDirPath = null;		
    	    	    
    	    try
    		{
    			obj = inputModuleData.getPrincipalData();
    		    msg = (Message) obj;
    		    amk = new MessageKey(msg.getMessageId(),msg.getMessageDirection());	
    		    
    		    Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "JAM-MAttachToDir| Custom-Java-Adapter-Module (localejbs/MAttachToDir) to store mutliple attachments to directory");
    		    		    
    		    //Get user-input as a "module.key" parameters from communication channels		    
    		    //Input-1:	'SapDirPath' to get folder path where attachments will be stored		    
    		    SapDirPath = (String) moduleContext.getContextData("SapDirPath");  
    		    Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "JAM-MAttachToDir| Directory Path input (SapDirPath)= " + SapDirPath);
    		    		    		    
    		    //Get the input attachment from the source message)
    		    Iterator itr = msg.getAttachmentIterator();
    		    while(itr.hasNext()){
    		    	//handle attachment
    		    	Payload payload = (Payload) itr.next();		
    		    			    	
    		    	//get attachment name
    		    	String atchnm = payload.getContentType();	//this gives output like "application/octet-stream;name=""fnam1.csv"
    		    	atchnm = atchnm.replaceAll("\"", "");		//replace all quoteString (i.e. ") with blank
    		    	int i1 = atchnm.lastIndexOf("=") + 1;
    		    	String atchmntNm = atchnm.substring(i1,atchnm.length());
    		    	Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "JAM-MAttachToDir| Attachment found as " + atchmntNm);
    		    	
    		    	//Write file to directory
    		    	byte [] inpbyt = payload.getContent();
    		    	File path = new File(SapDirPath + "/" + atchmntNm);
    		    	FileOutputStream fos = new FileOutputStream(path);
    		    	fos.write(inpbyt);
    		        fos.flush();
    		        fos.close();
    		        
    		        Audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "JAM-MAttachToDir| " + atchmntNm + " copied to folder " + SapDirPath);
    		    }
    		}catch (Exception e) {
    		      ModuleException me = new ModuleException(e);
    		      throw me;
    	    }		
    		return inputModuleData;	       
    	}	
    }
    

 

Steps to Create Java Adapter Module:

  1. Create an EJB project in “SAP NetWeaver Developer Studio 7.1
  2. Assign project names to the EJB and EAR projects
    • EJB Project Name: MAttachToDir_EJB.
    • EAR Project Name: MAttachToDir_EAR (check the option to add EAR project).
    • Click “Next”.
  3. Choose the correct EJB version.
    • Configure Module Project as → SAP EJB J2EE 1.4 Project.
    • Choose the EJB Module → 2.1.
    • Click “Next”.
    • Uncheck the flag to create the EJB client jar interfaces.
    • Click “Finish”
    • Please note, 2 projects will be created, an EJB project (MAttachToDir_EJB) and an EAR project (MAttachToDir_EAR).
  4. Create an Enterprise Bean in the EJB project
    • Right-click on the “MAttachToDir_EJB”.
    • Select: New → EnterpriseBean.
  5. Create the package and class for the EJB project
    • EJB Name:                   MAttachToDir
    • Bean Type:                   Select the dropdown “Stateless Session Bean
    • Default EJBPackage:   com.sap.adaptermodule
    • “generate default interfaces”  => “Uncheck” in checkbox
    •  “add optional interfaces”        => “Check” in checkbox
    • Click “Next“.
  6. Assign user-module interfaces | Component and Local Interfaces parameters:
    • Bean class:                   keep same value which appears default
    • Remote interface:         com.sap.aii.af.lib.mp.module.ModuleRemote
    • Home interface:            com.sap.aii.af.lib.mp.module.ModuleHome
    • Local interface:             com.sap.aii.af.lib.mp.module.ModuleLocal
    • LocalHome interface:   com.sap.aii.af.lib.mp.module.ModuleLocalHome
    • Service End point“:   => “Uncheck” in checkbox
    • Click ‘Finish’
  7. Examine and verify the content of ejb-jar.xml
    • On the left panel, navigate: EJB project → ejbModule → META-INF.
    • Double click on the “ejb-jar.xml”.
    • When the “ejb-jar.xml” is displayed on the right panel, select the “Enterprise Beans” tab at the bottom.
    • Expand “Session Beans” and select “MAttachToDir”.
    • All the local and remote interfaces should be displayed.
    • The “Service endpoint” should be empty. If not, then go to “Source” and edit the tag “<service-endpoint>”with blank value
  8. Enter the JNDI name in the ejb-j2ee-engine.xml
    • On the left panel, double click on the “ejb-j2ee-engine.xml”.
    • “Ejb-j2ee-engine.xml” will be displayed on the right panel
    • Expand “Session beans” and click on “MAttachToDir”
    • Enter the “JNDI Name”   for e.g. “MAttachToDir
    • Save the file.
  9. Include external libraries in the EJB project so the java class can be compiled
    • Right click on the EJB project.
    • Navigate to “Build Path”.
    • Select “Configure Build Path”.
    • In the Properties dialogue box, click the “Add Variable”.
    • Select the variable, “PI_AF_LIBS”, created earlier
    • Click on “Extend”.
  10. Enter the code for “MAttachToDirBean”
    • Copy and paste code from above to below project file
  11. Delete the package containing the Local and Remote interfaces from the build
    • Expand the “build” folder in the EJB project.
    • Delete all the folders created under build → classes → com → sap -> aii
    • Do not deletebuild → classes → com → sap -> adaptermodule”
  12. Configure the EAR project: The EAR file contains the following:
    • JAR file created from the EJB project.
    • It has configuration information of the libraries, services and interfaces that will be used by the user-module in the EJB.
    • It contains the SAP manifest file, which has unique identifiers for each specific EAR. The manifest information is generated uniquely each time it is modified.
    • Steps to Configure EAR:
      • Expand the EAR project.
      • Double-click on the “application-j2ee-engine.xml” file.
      • Failover Mode” => “Check” in checkbox
      • In the right panel, TAB “General”, click on the “Reference” and then on the “+” sign to “Add Elements”.
      • Click “Create New” in the dialog.
      • Add below elements as given in table
      • Reference target Reference type Reference target type Provider name
        engine.security.facade hard service sap.com
        engine.j2ee14.facade hard library sap.com
        com.sap.aii.af.svc.facade hard service sap.com
        com.sap.aii.af.ifc.facade hard interface sap.com
        com.sap.aii.af.lib.facade hard library sap.com
        com.sap.base.technology.facade hard library sap.com
      • Either go to TAB “Source” and paste below XML to add all tabular “Reference target” at once
      • <?xml version="1.0" encoding="UTF-8" standalone="no"?>
        <application-j2ee-engine 
        	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        	xsi:noNamespaceSchemaLocation="application-j2ee-engine.xsd">
        	<reference 
        		reference-type="hard">
        		<reference-target 
        			provider-name="sap.com" 
        			target-type="service">engine.security.facade</reference-target>
        	</reference>
        	<reference 
        		reference-type="hard">
        		<reference-target 
        			provider-name="sap.com" 
        			target-type="library">engine.j2ee14.facade</reference-target>
        	</reference>
        	<reference 
        		reference-type="hard">
        		<reference-target 
        			provider-name="sap.com" 
        			target-type="service">com.sap.aii.af.svc.facade</reference-target>
        	</reference>
        	<reference 
        		reference-type="hard">
        		<reference-target 
        			provider-name="sap.com" 
        			target-type="interface">com.sap.aii.af.ifc.facade</reference-target>
        	</reference>
        	<reference 
        		reference-type="hard">
        		<reference-target 
        			provider-name="sap.com" 
        			target-type="library">com.sap.aii.af.lib.facade</reference-target>
        	</reference>
        	<reference 
        		reference-type="hard">
        		<reference-target 
        			provider-name="sap.com" 
        			target-type="library">com.sap.base.technology.facade</reference-target>
        	</reference>
        	<fail-over-enable 
        		mode="disable" 
        		xsi:type="fail-over-enableType_disable"/>
        </application-j2ee-engine>
        
      • Save the file.
  13. Exporting SAP EAR File
    • Right Click on EAR project -> Export -> SAP EAR File
  14. Custom java adapter module has been created as EAR file “MAttachToDir_EAR.ear

 

Steps to Covert EAR files to SDA Files using NWCE-Tool:

  • From above steps, java adapter module EAR file “MAttachToDir_SDA.ear” gets created.
  • To use this module in SAP-PI, we need to convert it in to SDA file format with “.sda” extension.
  • Pr-requisites for EAR to SDA file conversion:
    • Java Jre1.6      Check Java Jre1.6 Directory (here C:\Program Files\Java\jre1.6.0_01)
    • nwcetool          Copy nwcetool folder in to C:\NWDS
    • Any local Windows system
  • Command line steps for EAR to SDA file conversion:
    • cmd>cd \
    • cd C:\NWDS\NWCE_TOOLS\tools\nwcetool
    • SET JAVA_HOME=C:\Program Files (x86)\Java\jre6
    • SET NWCETOOLLIB=C:\NWDS\NWCE_TOOLS\tools\lib
    • nwcetool.bat
    • createsda -n MAttachToDir -v sap.com -l MAttachToDir -c 2 -type J2EE C:\Users\User\Desktop\bLOG_jAM\MAttachToDir_EAR.ear C:\Users\User\Desktop\bLOG_jAM\MAttachToDir_SDA
    • Where
      • createsda -n <JNDI-Name> -v Vendor-Name -l <JNDI-Name> -c 2 -type J2EE <source EAR file path> <destination SDA file path>
  • Now rename file “MAttachToDir_SDA.ear” to “MAttachToDir_SDA.sda

 

Steps to Deploy SDA files in PI-Server:

  • SDA File deployment using Telnet in SAP-PI:
    1. Place the sda file (MAttachToDir_SDA.sda) in directory ‘/usr/sap/trans/EPS/in/’
    2. Update the file ‘/usr/sap/trans/EPS/in/deploylist.txt’ with the file to be deployed
    3. Use below commands
      • telnet localhost <port>
      • Username: <enter j2ee_admin user>
      • Passwrod: <*******>
      • deploy list=/usr/sap/trans/EPS/in/deploylist.txt version_rule=all
    4. Post Deployment, SDA file gets extracted in below SAP-PI’s folder path, having folder name as of JNDI-Name ‘MAttachToDir
      • /usr/sap/<systemID>/<instance>/j2ee/cluster/apps/sap.com/MAttachToDir/

Steps to check deployed custom Java-Adapter-Module in SAP-PI/PO server:

  • In SAP-PI, go to NWA link [ http://<sap-pi-host>:<sap-pi-port>/nwa ] and follow below steps:
    • In NWA -> Problem Management -> Java -> JNDI Browser
    • In NWA -> Operation Management -> System -> Start & Stop -> Java EE Applications
  • In SAP-PO, go to NWA link [ http://<sap-pi-host>:<sap-pi-port>/nwa ] and follow below steps:
    • In NWA -> search ‘JNDI’
    • or in NWA -> Troubleshooting -> Java -> JNDI Browser -> scroll down to search ‘MAttachToDir
    • or in NWA -> Troubleshooting -> Java -> System Information -> TAB ‘Component Info’ -> 2nd half TableGrid -> Search for Name ‘JNDI Name of Module’
    • In NWA -> Operation -> System -> Start & Stop -> Java EE Applications -> search ‘MAttachToDir

 

 

 

 

 

Thanks for reading………..hope this helps

Assigned Tags

      6 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Manoj K
      Manoj K

      Nice one Dilip. Clearly explained Step by Step.

      But i believe this module will work only in case NFS isn't it? Because i can see in your module implementation you are just building FilePath to the local server. In case you need to drop it to external server you need to implement FTP/SFTP connection to the server in the module code itself.

       

      Author's profile photo Dilip Kumar Krishnadev Pandey
      Dilip Kumar Krishnadev Pandey
      Blog Post Author

      Dear Manoj,

      Good Morning ...

      Yes, This module is working for SAP-Ecc-directory which is mapped/accessible to SAP-PI server.

       

      Thanks & Regards,

      Dilip.

      Author's profile photo Evgeniy Kolmakov
      Evgeniy Kolmakov

      Hi Dilip!

      And why not to use SFTP adapter for that at receiver's side? As I could see from your code, you consider your PI OS to be the Unix OS (by the way, avoid using hardcoded file path separators in any code, your module will raise exception on Windows OS, for example. Use "File.separator" instead).

      Regards, Evgeniy.

      Author's profile photo Dilip Kumar Krishnadev Pandey
      Dilip Kumar Krishnadev Pandey
      Blog Post Author

      Dear Evgeniy Kolmakov

      • Thanks for suggestion on filePath check, in module code we can give a condition if directory exists then proceed or throw module exception with message as "Directory not found".
      • We can use SFTP adapter at receiver side as this adapter supports multiple attachments, here no need of any custom module.
      • But in my case, I have to work with SAP-ECC-Directory which is accessible from SAP-PI using filePath reference only, so I have to use File-Receiver Channel and module is to make it compatible to handle multiple attachments.

      Thanks & Regards,

      Dilip.

      Author's profile photo Evgeniy Kolmakov
      Evgeniy Kolmakov

      Hi Dilip!

      1. I just wanted to point on the way to create more flexible, reusable and robust applications.

      2. Since you have access to your resource using java IO, this means that it is mounted to PI's file system. And most of Unix/Linux systems provide access to its file system using SSH by default. So you could use SFTP adapter to access PI file system without any additional SFTP server software required.

      Regards, Evgeniy.

      Author's profile photo Dilip Kumar Krishnadev Pandey
      Dilip Kumar Krishnadev Pandey
      Blog Post Author

      Dear Evgeniy Kolmakov

       

      About 2nd point, I will try this option.

       

      Thanks & Regards,

      Dilip.