Skip to Content
Technical Articles

SAP PI/PO – Java Mapping Create attachments – SFTP

Hello Folks on-premise,

Everything is good here or it’s time to fly (cloud)? 😀

I just would like to post one solution for a project that the requirement to write down the files in the SFTP server must follow the order – first PDF and the corresponding XML for each PDF’s.

Integration Diagram:

Outbound interface – Abap proxy with attachments (application/pdf and application/xml) in SAP PI transformation of reading stream attachments, create new attachments with the dynamic name of the XML file get previously in the message mapping using UDF – GET FILENAME and generating new attachments as (application/xml) setting the main payload as (application/xml), create the ICO with order processing mapping and two channels SFTP receiver because the attachments and the main payload contains Content-Type different and this is not possible to handle in the SFTP channel two different extensions –

You can develop a custom module adapter to solve it but in the duty of time, I did this solution.

Rules:

  1. Tag – <Dateiname>: Subelement of the element <Rechnung>. With version 2.0 it’s possible to attach more than one PDF file to a request (e.g. invoices, packaging lists). Additionally, the filenames of files attached to a request may now be chosen by the applicant without any restrictions. To be able to assign the files to a request, it’s necessary to indicate the name of the file in the new element <Dateiname>.
  2. The elements <Dateiname> and <Beglaubigen> need not to be included in the XML file under the following conditions:
    – There is only one PDF file which has to be attached to the request. In this case, the name of
    the PDF file must be equal to the one of the XML file (except the file extension)
    – The file to be attached to the request has also to be certified.
  3. If your XML file doesn’t contain the elements <Dateiname> and <Beglaubigen>, the XML file
    and the corresponding document must have the same filename (except the file extension)
    – If your XML file contains these two elements, you’re free to use any filename for the PDF files to be attached to the request. In this case, the element <Dateiname> must reflect the name of a corresponding file.
    – In any case, the additional documents must be uploaded to the server before uploading the corresponding XML file.

Clear for you? 

Let’s rock!

Integration Development Steps:

  1. Repository:

    • Imported Archives – JavaMapping
    • Message Mapping – UDF – SetFileName 
    • Two Operation Mappings – First – XML – Second: Payloads – PDF
  2. Directory:

    • ICO Receiver Interface
    • ICO Outbound Processing SFTP Channels

Process steps – Repository:

  • Repository: Imported Archives – JavaMapping

Rename Attachments java:

package yourpackage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Collection;

import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.Attachment;
import com.sap.aii.mapping.api.DynamicConfiguration;
import com.sap.aii.mapping.api.DynamicConfigurationKey;
import com.sap.aii.mapping.api.InputAttachments;
// import com.sap.aii.mapping.api.StreamTransformation;
// import com.sap.aii.mapping.api.StreamTransformationConstants;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus;

public class RenameAttachments extends AbstractTransformation {

	public static void main(String[] args) throws Exception {

		FileInputStream inFile = new FileInputStream("C:\\Users\\luzholar\\Desktop\\Payload.xml"); 
		FileOutputStream outFile = new FileOutputStream("C:\\Users\\luzholar\\Desktop\\Payload2.xml");

		new RenameAttachments().local(inFile, outFile);
	} 

	private void local(FileInputStream inFile, FileOutputStream outFile) throws Exception{

		String xmlPayload= "";
		xmlPayload="<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
		xmlPayload = xmlPayload.concat("<ns0:FileInfoGeneric xmlns:ns0=\"http://roche.com/rp/EM/CH/LAL/Common\">");
		xmlPayload = xmlPayload.concat("<Receivers>");
		xmlPayload = xmlPayload.concat("<Receiver/>");
		xmlPayload = xmlPayload.concat("</Receivers>");
		xmlPayload = xmlPayload.concat("<Name>");
		xmlPayload = xmlPayload.concat("test");
		xmlPayload = xmlPayload.concat("</Name>");
		xmlPayload = xmlPayload.concat("</ns0:FileInfoGeneric>");

		outFile.write(xmlPayload.getBytes("UTF-8"));

	}

	private static final DynamicConfigurationKey KEY_FILENAME = DynamicConfigurationKey.create("http://sap.com/xi/XI/System/File","FileName");

	public void transform(TransformationInput in, TransformationOutput out) throws StreamTransformationException {
		try {
			AuditLogHelper logger = AuditLogHelper.getInstance(in.getInputHeader().getMessageId());
			logger.log("**** Java Mapping Starting ******", AuditLogStatus.SUCCESS);
			OutputStream outputstream = out.getOutputPayload().getOutputStream();
			String xmlPayload= "xml";
			DynamicConfiguration conf = in.getDynamicConfiguration();
			String fileName = "";
			fileName = conf.get(KEY_FILENAME);
			logger.log("Dynamic name of XML: "+ fileName, AuditLogStatus.SUCCESS);
			String fileNamepdf = fileName.replace(".xml", "");
			logger.log("Dynamic name of PDF Attachment(s): "+ fileNamepdf, AuditLogStatus.SUCCESS);
			Object[] arrayObj = null;
			String attachmentID = null;
			InputAttachments inputAttachments = in.getInputAttachments();
			if (inputAttachments.areAttachmentsAvailable()) {
				// gets the attachmentIds and store it in an Object array
				Collection<String> collectionIDs = inputAttachments.getAllContentIds(true);
				arrayObj = collectionIDs.toArray();
				// Object[] arrayObj = collectionIDs.toArray();
				// Loops at the input attachments to get the content of the attachment and then
				int counter = 1;
				for (int i = 0; i < arrayObj.length; i++) {
					attachmentID = (String) arrayObj[i];
					Attachment attachment = inputAttachments.getAttachment(attachmentID);
					byte[] attachmentBytes = attachment.getContent();
					logger.log("Content-Type: "+ attachment.getContentType(), AuditLogStatus.SUCCESS);
					Attachment newopAttachment = out.getOutputAttachments().create(fileNamepdf+"_" + counter +".pdf", "application/pdf; name="+fileNamepdf+"_" +counter +".pdf",attachmentBytes);
					out.getOutputAttachments().setAttachment(newopAttachment);
					out.getOutputAttachments().removeAttachment(attachmentID);
					logger.log("New Attachment: "+ newopAttachment.getContentType(), AuditLogStatus.SUCCESS);
					counter++;
				}
				logger.log("**** Java Mapping End ******", AuditLogStatus.SUCCESS);
			}
			outputstream.write(xmlPayload.getBytes("UTF-8"));
		} catch (Exception e) {
			throw new StreamTransformationException("Error: " + e.getMessage());
		}
	}
}
  • Repository: Imported Archives – JavaMapping

Audit Logger java:

import com.sap.engine.interfaces.messaging.api.MessageDirection;
import com.sap.engine.interfaces.messaging.api.MessageKey;
import com.sap.engine.interfaces.messaging.api.PublicAPIAccessFactory;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditAccess;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus;

public class AuditLogHelper {

	private static final String DASH = "-";
	
	private static boolean isInitialize = false;
	
	private static AuditAccess auditAccess;

	private MessageKey messageKey;
	
	private static void initialize() throws Exception {
		auditAccess = PublicAPIAccessFactory.getPublicAPIAccess().getAuditAccess();
	}
	
	private AuditLogHelper(MessageKey messageKey) throws Exception {
		this.messageKey = messageKey;
	}

	public static AuditLogHelper getInstance(String messageId) throws Exception {
		
		if(!isInitialize)
			initialize();
		
		String msgUUID = messageId.substring(0, 8) + DASH + messageId.substring(8, 12) + DASH
				+ messageId.substring(12, 16) + DASH + messageId.substring(16, 18) + messageId.substring(18, 20) + DASH
				+ messageId.substring(20, 32);
		
		MessageKey messageKey = new MessageKey(msgUUID, MessageDirection.OUTBOUND);
		
		return new AuditLogHelper(messageKey);
	}

	public void log(String message, AuditLogStatus status) {
		auditAccess.addAuditLogEntry(messageKey, status, message);
	}

	public void setMessageKey(MessageKey messageKey) {
		this.messageKey = messageKey;
	}

}
  • Repository: Message Mapping – UDF – SetFileName

First rule logic from the requirement – FileName based in the XML tags:

Why you implement this logic just to setup the filename?

Check the rules above.

  • Message Mapping – UDF – SetFileName 

I set up the UDF in the ROOT of the XML – ROOT – LOGIC + UDF – ROOT.

UDF – setFileName:

MappingTrace trace;
trace = container.getTrace();
trace.addInfo("FileName: " + FName);

try
{
	DynamicConfiguration conf=(DynamicConfiguration) container.getTransformationParameters().get(StreamTransformationConstants.DYNAMIC_CONFIGURATION);

	// SAP standard
	DynamicConfigurationKey key=DynamicConfigurationKey.create("http://sap.com/xi/XI/System/File","FileName");
	if (conf != null)	{ 
			 conf.put(key,FName); 
	 }

	// SAP SFTP Standard
	DynamicConfigurationKey sapSFTPkey=DynamicConfigurationKey.create("http://sap.com/xi/XI/System","FileName");
	conf.put(sapSFTPkey,FName); 

	// SFTP Seeburger
	DynamicConfigurationKey sftpkey=DynamicConfigurationKey.create("http://seeburger.com/xi/common","dtSubject");
	conf.put(sftpkey,FName);

	DynamicConfigurationKey sftpAN=DynamicConfigurationKey.create("http://seeburger.com/xi/common","dtAttachmentName");
	conf.put(sftpAN,FName);

	// AS2 Seeburger
	
	DynamicConfigurationKey as2Key=DynamicConfigurationKey.create("http://seeburger.com/xi/AS2","dtAS2FileName");
	conf.put(as2Key,FName);

}
catch (Exception e)
{
	return ("ERROR");
}

return (FName);
  • Repository: Two Operation Mappings – First – XML – Second: Payloads – PDF

Set XML – The main payload will be set up here so no read the attachments

Set PDF – Message Mapping to generate the Dynamic Configuration of the PDF files and java mapping to rename the attachments correct based on this dynamic name definition:

Process steps – Directory:

  • ICO Receiver Interface

With this correct order first, write the PDF or PDF files with the same name as the main payload XML, which will be ok.

This is the tricky part to attend the requirement of first save the PDF(s) files related to the main XML before the XML always.

You just need to order the two mappings to be processed by the pipeline and mark (Maintain Order at Runtime) because in case of the receiver SFTP adapter receiver of PDF will be off, the messages will get stuck and will not store the XML file in the another SFTP adapter receiver.

  • ICO Outbound Processing SFTP Channels

Why two receiver channels?

Basically, it’s a tricky point, the name of XML and PDF must be the same in many conditions but the extension will be always different in this case it’s not possible to handle in only one channel because the name of PDF files will be always – File.xml.pdf

I believe you can solve it with a custom module adapter but I was without time.

Testing with SOAPUI Call with attachments – Simulating the PROXY with attachments:

SOAPUI with attachments:

Name of the attachments:

  • bilet_94186535.pdf
  • Bilet_elektroniczny_ohgyrc9d-7747__BiletomatPL.pdf

After the java mapping execution:

TagName:

  • <Dateiname>TEST BLOG JAVA MAPPING ATTACHMENTS</Dateiname>

Attachments:

  • TEST BLOG JAVA MAPPING ATTACHMENTS_1.pdf
  • TEST BLOG JAVA MAPPING ATTACHMENTS_2.pdf

Logs from Audit Logger java:

SFTP Server:

So I hope you enjoy, time to go back cloud.

Kind Regards,

Viana.

5 Comments
You must be Logged on to comment or reply to a post.
  • Hi Ricardo!

    Thanks for sharing this!

    One question: are you sure that correct interfaces sequence can be maintained without using the option “Maintain Order at Runtime”?

    What happens, for example, if the different channels are used for receiving interfaces and the channel for the first interface is stopped?

    Regards, Evgeniy.

    • Hello Evgeniy,

      Good point I forgot to mention that you must check box this because in case of the communication channel PDF receiver it’s off the channel XML will be store the XML.

      Sample:

      I stop the channel the SFTP Receiver Channel for PDF only, but because of MainTain Order at Runtime, the messages start to be with status ON-HOLDING:

      Monitoring Status:

      SFTP:

      Write blogs in the weekends we can do small mistakes hehehehe.

      Thanks.

      CYA.

  • Hi Ricardo,

    Nice blog and after long tome I saw few blogs from you, related to JAVA mappings.

    I have one question, what is use of Audit Logger java ?

    Is there any specific reason to use this ?

    If you want to log in JAVA, then you just need to add below code (as you are extending superclass AbstractTransformation).

    getTrace().addInfo(“—-Java Mapping—-: Conversion started”);

    Thanks,

    Bijayashree

    • Hello Bijayashree,

      Add trace logging – UDF – JAVA Mapping:

       

      Logging typically means the recording of implementation level events that happen as the program is running (methods get called, objects are created, etc.). As such it focuses on things that interest programmers.

      Valid add – getTrace().addinfo(“—-Java Mapping—-: Conversion started”); when you want to test your java mapping already imported in SAP PI to check the parsing using DOM or SAX.

      Audit logging – JAVA Mapping:

       

      Auditing is about recording domain-level events: a transaction is created, a user is performing an action, etc. In certain types of applications, there is a legal obligation to record such events.

      I add this type of logging because you are not able to test in runtime the Operation Mapping with Attachments inside SAP PI and I would like to see in the logging level msg if the attachments from the incoming message it’s in the correct criteria of the requirements.

      Requirement: Mainpayload as extension(xml – content-type: application/xml) with the same name of the attachment(s) (pdf – content-type: application/pdf).

      I hope this clarifies to you the motives that I use this type of logging mechanism.

      Thanks,

      KR.

      Viana.