Creating A PDF Document within a BPEL Process

Quite recently I had this requirement in one of my project to create a PDF snapshot from a BPEL Process. The PDF document was required to be archived and contain a brief detail of key business indicators for the order and also whether the was processed manually or automatically.

The business requirement thus set it was now evident that we needed to define a schema to extract the key order indicators and use it in an Embedded Java activity inside a BPEL process to call a custom piece of Java code that create a PDF document.

Defining the schema

Here is snapshot of the schema I choose to create to describe the Order Indicators for a Sales Order Instance.

image

The Order message is intercepted in the bpel process to determine whether it is to be processed manually or automatically.

A first Look at the PDF

Creating an instance and letting the Order process manually I was able to derive a PDF capturing the order summary at an archived location. The below screenshot shows how the document looked like.

image

Creating the Process

The first step in the process would be to instantiate a salesOrderIndicator variable based on the above schema and extract information from the Sales Order payload inside the BPEL process.

I created a custom Java class to print a PDF document using the iText api for Java.

The library can be obtained from sourceforge here

Create a Java project in JDeveloper and name it OrderApprovalDocument. Add the Itextpdf-5.1.1.jar to the project classpath.

image

The OrderApprovalDocument class will create an instance of PdfWriter, take in value from the BPEL JAVA Embedded activity and create the pdf document in a tabular format.

package com.beatech.salesapp;

import com.itextpdf.text.BadElementException;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.Barcode;
import com.itextpdf.text.pdf.BarcodeEAN;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;

import java.awt.Color;

import java.io.FileOutputStream;

import java.util.Date;
import java.util.Iterator;
import java.util.Map;

public class OrderApprovalDocument {
public OrderApprovalDocument() {
super();
}

private static Font catFont =new Font(Font.FontFamily.HELVETICA, 11, Font.BOLD);
private static Font redFont =new Font(Font.FontFamily.TIMES_ROMAN, 9, Font.NORMAL, BaseColor.RED);
private static Font subFont =new Font(Font.FontFamily.HELVETICA, 6, Font.BOLD);
private static Font smallBold =new Font(Font.FontFamily.HELVETICA, 8, Font.BOLD);

public void createPDFSummary( Map varMap, String approvalStatus)
{

String fileName = "c:/Arrun/OrderApprovalSnapshot_"+(String)varMap.get("PURCHASE ID")+".pdf";

try {
Document document = new Document();
PdfWriter writer =PdfWriter.getInstance(document, new FileOutputStream(fileName));
document.open();
addDocumentMetaData(document);
addDocumentTitle(document,varMap);
addContent(document,writer,varMap, approvalStatus);
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}

// iText allows to add metadata to the PDF which can be viewed in your Adobe Reader under File -> Properties

private static void addDocumentMetaData(Document document) {
document.addTitle("Order Approval Summary");
document.addSubject("Sales Order Summary Document");
document.addKeywords("Sales Orchestraction Project,Order Approval Summary");
document.addAuthor("Sales Orchestraction Project");
document.addCreator("Sales Orchestraction Project");
}

private static void addDocumentTitle(Document document, Map contentMap) throws DocumentException {
Paragraph preface = new Paragraph();
// Adding one empty line
addEmptyLine(preface, 1);
// Adding a Document header
preface.add(new Paragraph("APPROVAL FLOW FOR ORDER : " + (String)contentMap.get("PURCHASE ID"), catFont));
addEmptyLine(preface, 1);
// A Small body: Report generated by  _Name, _Date
preface.add(new Paragraph("Report Generated by Order Approval Flow,  " +new Date(), redFont));
addEmptyLine(preface, 2);
document.add(preface);

}

private static void addContent(Document document, PdfWriter writer, Map contentMap, String approvalStatus) throws DocumentException {
// Add a table
createTable(document, writer,contentMap, approvalStatus);
}

private static void createTable(Document document, PdfWriter writer, Map varMap, String approvalStatus) throws BadElementException, DocumentException {
PdfPTable table = new PdfPTable(3);
table.setTotalWidth(500);
table.setLockedWidth(true);

PdfPCell c1 = new PdfPCell(new Phrase("ORDER APPROVAL SUMMARY", new Font(smallBold)));
c1.setColspan(3);
c1.setFixedHeight(40);
c1.setBorderColor(new BaseColor(Color.RED));
c1.setBorder( Rectangle.BOX);
c1.setBorderWidth(1);
c1.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(c1);
PdfContentByte cb = writer.getDirectContent();
BarcodeEAN codeEAN = new BarcodeEAN();
codeEAN.setCodeType(Barcode.EAN13);

String s= "000000000000"+(String)varMap.get("PURCHASE ID"); // twelve zeros prepended

codeEAN.setCode(s.substring(s.length()-13));
Image imageEAN = codeEAN.createImageWithBarcode(cb, null, null);
c1 = new PdfPCell(imageEAN,false);
c1.setHorizontalAlignment(Element.ALIGN_RIGHT);
c1.setColspan(3);
c1.setBorder(1);
c1.setFixedHeight(5);
table.addCell(c1);

c1 = new PdfPCell(new Phrase("ORDER ID",new Font(smallBold)));
c1.setBorderColor(new BaseColor(Color.RED));
c1.setBorder( Rectangle.BOX);
c1.setBorderWidth(1);
c1.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(c1);

c1 = new PdfPCell(new Phrase((String)varMap.get("PURCHASE ID"),new Font(smallBold)));
c1.setBorderColor(new BaseColor(Color.RED));
c1.setBorder( Rectangle.BOX);
c1.setBorderWidth(1);
c1.setColspan(2);
c1.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(c1);

c1 = new PdfPCell(new Phrase("KEY INDICATORS",new Font(smallBold)));
c1.setRowspan(8);
c1.setBorderColor(new BaseColor(Color.RED));
c1.setBorder( Rectangle.BOX);
c1.setBorderWidth(1);
c1.setVerticalAlignment(Element.ALIGN_CENTER);
c1.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(c1);
Iterator entries = varMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
c1 = new PdfPCell(new Phrase( (String)entry.getKey(),new Font(subFont)));
c1.setBorderColor(new BaseColor(Color.RED));
c1.setBorder( Rectangle.BOX);
c1.setBorderWidth(1);
table.addCell(c1);

c1 = new PdfPCell(new Phrase((String)entry.getValue(),new Font(subFont)));
c1.setBorderColor(new BaseColor(Color.RED));
c1.setBorder( Rectangle.BOX);
c1.setBorderWidth(1);
c1.setBackgroundColor(new BaseColor(255,181,188));
c1.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(c1);

}

c1 = new PdfPCell(new Phrase("APPROVAL STATUS",new Font(subFont)));
c1.setHorizontalAlignment(Element.ALIGN_CENTER);
c1.setBorderColor(new BaseColor(Color.RED));
c1.setBorder( Rectangle.BOX);
c1.setBorderWidth(1);
table.addCell(c1);
c1 = new PdfPCell(new Phrase(approvalStatus,new Font(subFont)));
c1.setHorizontalAlignment(Element.ALIGN_CENTER);
c1.setBorderColor(new BaseColor(Color.RED));
c1.setBorder( Rectangle.BOX);
c1.setBorderWidth(1);
c1.setBackgroundColor(new BaseColor(227,7,7));
c1.setColspan(2);
table.addCell(c1);
document.add(table);

}
private static void addEmptyLine(Paragraph paragraph, int number) {
for (int i = 0; i < number; i++) {
paragraph.add(new Paragraph(" "));
}
}

}

Feel free to add your own custom style to the pdf. You can even add your corporate logo into the snapshot. 🙂

Secondly use the Embedded Java Activity from the BPEL process to call this custom class and pass a Map containing the key indicators for the sales order instance.

You can even pass the entire XML from the embedded java activity and unmarshall the XML in the custom class. Its a matter of choice.

image

Here is the java code for the embedded activity

System.out.println("<<<=====================Entering JAVA Embedding===============================>>>");
Map variableMap = new HashMap();
try{
String approvalStatus ="ORDER AUTOMATICALLY APPROVED";
// Setting the Map
variableMap.put("FULFILMENT ID",((XMLElement)getVariableData("salesOrderIndicator","/ns12:SALOrderIndicator/ns12:fulfillmentID")).getFirstChild().getNodeValue());

variableMap.put("ORDER GUID",((XMLElement)getVariableData("salesOrderIndicator","/ns12:SALOrderIndicator/ns12:orderGUID")).getFirstChild().getNodeValue());

variableMap.put("PURCHASE ID",((XMLElement)getVariableData("salesOrderIndicator","/ns12:SALOrderIndicator/ns12:purchaseID")).getFirstChild().getNodeValue());

variableMap.put("ORDER AMOUNT",((XMLElement)getVariableData("salesOrderIndicator","/ns12:SALOrderIndicator/ns12:orderAmount")).getFirstChild().getNodeValue());

variableMap.put("DISCOUNT AMOUNT",((XMLElement)getVariableData("salesOrderIndicator","/ns12:SALOrderIndicator/ns12:discountAmount")).getFirstChild().getNodeValue());

variableMap.put("TAX AMOUNT",((XMLElement)getVariableData("salesOrderIndicator","/ns12:SALOrderIndicator/ns12:taxAmount")).getFirstChild().getNodeValue());

variableMap.put("PROMISED SHIP TIME",((XMLElement)getVariableData("salesOrderIndicator","/ns12:SALOrderIndicator/ns12:promisedShipDateTime")).getFirstChild().getNodeValue());

variableMap.put("BOOKING TIME",((XMLElement)getVariableData("salesOrderIndicator","/ns12:SALOrderIndicator/ns12:bookingDateTime")).getFirstChild().getNodeValue());

if(((XMLElement)getVariableData("ruleProcessingResponse","payload","/ns20:callFunctionStatefulDecision/ns20:resultList/ns21:OrderClass/ns21:isManualOrder")).getFirstChild().getNodeValue()  == "true")
{
approvalStatus= "MANUAL APPROVAL NEEDED";
}
// Calling the Custom Class with the Map and approval status
OrderApprovalDocument createDoc = new OrderApprovalDocument();
createDoc.createPDFSummary(variableMap,approvalStatus);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("<<<=====================Leaving JAVA Embedding===============================>>>");

Open the .bpel file inside the composite and import the required classes for the Java Embedding activity using the <bpelx:exec/> extension.

image

Create a JAR from the custom java class project as OrderApprovalDocument.jar and add it to the classpath of the composite project.

image

Also copy the OrderApprovalDocument.jar into the SCA-INF\lib directory of the project inside the composite application.

Deploy the onto the soa server of the SOA Suite 11g runtime domain and test the process with a simple order message.

You can see the generated PDF in the directory mentioned in the custom java class. The pdf file is appended with the instance order number.

.

7 thoughts on “Creating A PDF Document within a BPEL Process

  1. Pingback: Archiving Audit Diagrams as Images in Oracle SOA Suite BPM Processes « Oracle Technologies Premier

    • Hi Jon, I was not aware of the XSLFO approach and it looks very interesting. However the documentation available to use it to generate a PDF from a BPEL process is not exhaustive. Is there a way that PDF’s can be formatted?

      Like

  2. I used an alternative approach for achieving the same.
    I wrote the same java code using iText Pdf jars and exposed that java code as a web service which was deployed on the same weblogic server.
    Hence, now, all services can use the same external java as a Partner link in their BPEL.

    The same can be done using Apache XSL-FO too.

    HTH.

    Like

  3. Hi,i want to say something.
    1. First, explore any standard Java 2 PDF generator APIs / Softwares. Apache may have one. In my earlier project, I used Open Source iText APIs, to create really very Complex PDF documents totally from Java. It has complex Fonts, colors, Tables, Graphichs, add images, header, footers etc etc including barcodes also in pdf itself.
    http://itextpdf.com/itext.php
    http://itextpdf.com/examples/iia.php?id=12
    http://pdfbox.apache.org/ (From Apache)
    2. So first develop a simple HelloWorld sample using one of above APIs and use the data in this to generate the PDFs, Once you explore and do all this, you are all set.
    Now coming to your requirement:
    1. Create a XSD Schema, that takes input data elements that should go into PDF file. Create a Response XSD schema also, that may send back details of files created etc etc.
    2. Create a WebService with one service, one port and couple of operations. Example create one operation like generatePdf(). Use above schema elements. Create the WebService. NOW in the actual generated Service class, for this Operation use your hello world sample code to create the PDFs. The most important thing is the location to Store the PDF File. you can store it in Database table or in File System. If it is FileSystem, make sure you can control the location using some properties file. Because it can be on Windows or Unix. So this should be dynamic.
    3. First Run the WebService client and pass the data and make sure that the pdf file is created in the location you specified.
    Now comes the actual BPEL process where you use all the above. Create a BPEL process that calls this webservice and passes in the input payload. WebService generats the pdf file (if No Errors) in a specific laotion. Now have a FILE Adapater component read this file and send the response down to further process. I hope you got the point.
    The important thing is generation of PDF file from Java which any BPEL or Oracle cannot do by default. You should do this using some java apis. Once file is there, you can pretty much do anything uisng BPEL or WebService.

    Like

  4. Hi,I have the requirement in soa bpel to read the pdf attachment and send/pass that pdf to osb prxoy attachment variable

    Can you please help me here how to declare the attachments in soa. i will execute the soa service by browsing that attachment from soapUI then that should pass the attachment pdf to osb proxy attachment variable ($attachments).

    Thanks
    Ramanath

    Like

If you have any comments, suggestions or feedback about the post, please feel free to type it here and I will do my best to address them asap