Skip to Content
Technical Articles
Author's profile photo stephen xue

ABAP Proxy retrieve PDF from CPI and have it printed locally

Introduction

This is a process recently implemented. I found part of the technology is a bit hard to be searched on the google. Therefore plan to share them out.

This is the overview process

  1. process starts from a S4HANA report. it sends a PDF retrieving request to CPI in form of outbound proxy
  2. once received the request, CPI sends the request to a 3rd party service
  3. The 3rd party service feedbacks the PDF stream
  4. CPI set the PDF stream as an attachment to the proxy response
  5. S4HANA report gets the response and extracts the PDF attachment
  6. S4HANA report sends the PDF to a printer.

In scope:

  • Groovy script deriving the pdf stream and attaching the PDF as attachment to the message
  • Synchronous Outbound ABAP proxy getting attachment from the response
  • Print PDF to printer directly
  • Printer configuration

Out of scope:

  • XI Sender Adapter configuration
  • ABAP backend Integration Engine Configuration
  • CPI iFlow building
  • Make synchronous call by using outbound ABAP proxy

Technical Details

  1. Parse PDF stream from payload by using groovy.

we might get bellowing result by consuming an API. A response with only PDF raw data. no xml, no soap, no json in the payload.

if we analyse the response raw data, it should be as below:

again, there is no soap envelope, no xml, no json, just a few scripts with binary data.

if the source stream has been parsed by using the groovy script below, it will break the file.

Message processData(Message message, def testFlag = null) {
    def body = message.getBody(Reader);
    //def body = message.getBody() as String;
    def sourceRequest = body.getText();

    def targetRequest = Mapping(sourceRequest,testFlag);
    message.setHeader('Content-Type',CONTENT_TYPE);
    message.setBody(targetRequest);
    return message;
}

instead, this is the correct way to retrieve the PDF stream. The type java.io.InputStream is to be used here , rather than the java.io.Reader

Message processData(Message message, def testFlag = null) {
    def body       = message.getBody(InputStream);
    //def body = message.getBody() as String;
    def pdfBytes   = body.getBytes();
}

 

2. Set the PDF as an attachment to the response message

this is the full source code,

import com.sap.gateway.ip.core.customdev.util.Message
import groovy.transform.Field
import groovy.xml.MarkupBuilder
import org.apache.camel.impl.DefaultAttachment
import javax.mail.util.ByteArrayDataSource

@Field String CONTENT_TYPE   = 'text/xml;charset=UTF-8';
@Field String MIME_TYPE_PDF  = 'application/pdf';
@Field String FILE_NAME      = 'Attached File.pdf';
Message processData(Message message, def testFlag = null) {
    // 1. Parse PDF Stream
    def body       = message.getBody(InputStream);
    def pdfBytes   = body.getBytes();
    
    // 2. Attach PDF as attachment to the message
    def dataSource = new ByteArrayDataSource(pdfBytes, MIME_TYPE_PDF);
    def attachment = new DefaultAttachment(dataSource);
    message.addAttachmentObject(FILE_NAME, attachment);

    // 3. This is a normal mapping for response payload
    def documentId = message.getProperties().get('DocumentID');
    def targetRequest = Mapping(documentId, testFlag);

    message.setHeader('Content-Type',CONTENT_TYPE);
    message.setBody(targetRequest);
    return message;
}

String Mapping(def order, def testFlag = null) {
    def xmlWriter = new StringWriter();
    def xmlMarkup = new MarkupBuilder(xmlWriter);
    xmlMarkup
            .'ns0:SalesOrderCreateResponse'('xmlns:ns0': 'urn:yourcompany.com:sapecc:lead2order') {
                Order(order);
            }
    return xmlWriter.toString();
}

The program consists of three parts.

1st and 2nd parts are parsing PDF and attached PDF to the message.

3rd part is doing a normal mapping in order to recall the normal mapping memory. yes, you can used them together in one program.

 

3. Extract PDF from response message

i found there are lots of blogs introducing how to attach a file to the response of an Inbound ABAP proxy(server proxy), however a bit few on extracting attachment from the response of an Outbound ABAP proxy(client proxy). They are very similar.

DATA:
  lv_pdf          TYPE xstring,                          " PDF content in binary data
  lr_proxy        TYPE REF TO zco_sales_order,           " Synchronous outbound proxy
  lr_attachments  TYPE REF TO if_wsprotocol_attachments,
  lt_attachments  TYPE        prx_attach.
FIELD-SYMBOLS:
  <lr_attachement>  TYPE REF TO if_ai_attachment.
CREATE OBJECT lr_proxy.
* The calling of outbound proxy has been omitted here
* It should be very similar as below
*          lr_proxy->order_create_request(
*            EXPORTING
*              output = ls_request
*            IMPORTING
*              input  = ls_response
*          ).
lr_attachments   ?= lr_proxy->get_protocol( if_wsprotocol=>attachments ).
lt_attachments    = lr_attachments->get_attachments( ).
READ TABLE lt_attachments INDEX 1 ASSIGNING <lr_attachement>.
IF sy-subrc EQ 0.
  lv_pdf = <lr_attachement>->get_binary_data( ).
ENDIF.

as you can see, different from inbound proxy, there is no instance of server context to be retrieved.

Checking if the attachment is working in the response

once the message has been receivd by ABAP integration engine, the attachment can be viewed in the tcode SXI_MONITOR

4. Print PDF directly to the printer

There are lots of information on how to print a document from a script form/smart form/interactive form. This is a very mature technology in the ABAP stack. My feeling is that, as there are more and more APIs come into being, we might face the situations to directly print document without any forms to the printer more frequently.

This is another blog of mine introducing print label to Zebra printer directly.

This is the source code for printing a PDF to a printer

    CALL FUNCTION 'ADS_CREATE_PDF_SPOOLJOB'
      EXPORTING
        printer           = p_prt  "Printer name supporting PDF device type
        pages             = 1
        pdf_data          = lv_pdf "XSTRING internal table
        immediate_print   = 'X'
      EXCEPTIONS
        no_data           = 1
        not_pdf           = 2
        wrong_devtype     = 3
        operation_failed  = 4
        cannot_write_file = 5
        device_missing    = 6
        no_such_device    = 7
        OTHERS            = 8.

The variable lv_pdf should be populated in the previous part with PDF binary stream.

The variable p_part is in TYPE rsposeldevice, the full device name.

5. Printer Configuration

I assume this is a normal skill. just in case you do not know it.

Choose the device type: SAPWIN.

note: PDF1 might not work here. coz device type PDF1 does convert from normal data into PDF. however for this case, the data is already in PDF format.

choose Berkeley protocol as access method

 

Conclusion

Groovy has a different way to derive PDF stream than normal xml/json message;

There is a way to set PDF as attachment to message back to ABAP proxy;

Extract attachment from the response of Outbound Proxy is very similar to attach the files to response message of inbound proxy, but there is a tiny difference on getting the context instance;

Print the PDF to printer by using a FM;

 

 

 

Assigned Tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.