Personal Insights
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:
- Create an EJB project in “SAP NetWeaver Developer Studio 7.1”
- 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”.
- 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).
- Create an Enterprise Bean in the EJB project
- Right-click on the “MAttachToDir_EJB”.
- Select: New → EnterpriseBean.
- 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“.
- 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’
- 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
- 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.
- 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”.
- Enter the code for “MAttachToDirBean”
- Copy and paste code from above to below project file
- 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 delete ” build → classes → com → sap -> adaptermodule”
- 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.
- Exporting SAP EAR File
- Right Click on EAR project -> Export -> SAP EAR File
- 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:
- Place the sda file (MAttachToDir_SDA.sda) in directory ‘/usr/sap/trans/EPS/in/’
- Update the file ‘/usr/sap/trans/EPS/in/deploylist.txt’ with the file to be deployed
- Use below commands
- telnet localhost <port>
- Username: <enter j2ee_admin user>
- Passwrod: <*******>
- deploy list=/usr/sap/trans/EPS/in/deploylist.txt version_rule=all
- 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
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.
Dear Manoj,
Good Morning ...
Yes, This module is working for SAP-Ecc-directory which is mapped/accessible to SAP-PI server.
Thanks & Regards,
Dilip.
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.
Dear Evgeniy Kolmakov
Thanks & Regards,
Dilip.
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.
Dear Evgeniy Kolmakov
About 2nd point, I will try this option.
Thanks & Regards,
Dilip.