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.
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.
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.
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.
Here is the java code for the embedded activity
Open the .bpel file inside the composite and import the required classes for the Java Embedding activity using the <bpelx:exec/> extension.
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.
Create a JAR from the custom java class project as OrderApprovalDocument.jar and add it to the classpath of the composite project.
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.