February 5, 2010

Can we recover the failed Bursting cases?

This is valid requirement. When we burst thousands of reports and some of them fail at document generation stage or delivery stage. We should be able to re-run the process for failed cases only.

This functionality is available in latest Bursting Engine but it was not documented. Since it was not documented it is not officially supported. Those who want to give a try, here are the steps.

Make sure you define the key attribute at Document level in bursting control file. The key should be unique for each document.

bc.jpg

Run the Bursting process in normal mode. After Bursting process complete, getRecoveryKeys() method of BurstingProcessorEngine returns the Vector which hold keys for all failed documents. Serialize this vector to file system as XML or simple text file. You need to write your own code for writing and reading these keys from file system.

try
{
//Logger.setLevel(Logger.STATEMENT);
BurstingProcessorEngine bp = new BurstingProcessorEngine();
bp.setTempDirectory("c:\\burst\\temp");
bp.setXMLAPI("c:\\burst\\InvoiceBurstingControlFile.xml");
dp.setData("c:\\burst\\InvoceData.xml");
Properties prop= new Properties();
prop.put("user-variable:EMAIL","xxxxxxx@xxxx.com");
dp.setConfig(prop);
dp.process();
Vector recoveryKeys = dp.getRecoveryKeys();
//Save these recovery keys to file system, we need to reconstruct the vector during recovery process//
dp.deletTemporaryOutputFiles();
} catch (Exception e)
{
Logger.log(e);
}

During recovery process, reinitialize the Vector from the earlier file, which hold all the failed keys. Set these keys to BurstingProcessEngine and run it in recovery mode. This will only process the failed cases.

try
{
//Logger.setLevel(Logger.STATEMENT);
BurstingProcessorEngine bp = new BurstingProcessorEngine();
bp.setTempDirectory("c:\\burst\\temp");
bp.setXMLAPI("c:\\burst\\InvoiceBurstingControlFile.xml");
dp.setData("c:\\burst\\InvoceData.xml");
Properties prop= new Properties();
prop.put("user-variable:EMAIL","xxxxxxx@xxxx.com");
dp.setConfig(prop);
Vector recoveryKeys=null;
//initialize Vector with recovery keys extracted during main process//
dp.setRecoveryKeys(recoveryKeys);

dp.process();
dp.deletTemporaryOutputFiles();
} catch (Exception e)
{
Logger.log(e);
}

Please try and let me know your experience.

January 22, 2010

Bursting: Setup email body content as HTML for Email Delivery

One of the forum member asked this question "How does one send the body of the email as html instead of text?".

We can set the content-type attribute of message section as 'html/text'. This will treat the email body content as HTML.

Here is an example:

ctrl4.jpg

December 8, 2009

Dynamic SQL query in Data Template

Recently I came across couple of questions on dynamic/conditional query within Data template and those could be easily answered through usage of substitute variables.

So what is substitute variable and how to define and use it?

These are the placeholders and get replace with the actual values before the query execution. We can use Data Template parameters, global variables from PL/SQL default package, element names from data structure section and lexical (KFF variables) as substitute variables.

The substitute variable could be use to define the complete or part of SQL query. In following example, the "where clause" of the query set dynamically based on the "pwherecaluse" global variable defined in default PL/SQL package (Employee).

dt1.jpg
Lets have a look on BeforeReportTrigger function from employee package, which is default pl/sql package for this Data Template.
dt2.jpg
Data Engine executes the BeforeReportTrigger trigger before executing any SQL query. The pwhereclause variable will be set based on the p_DEPTNO parameter. If the p_DEPTNO is 10, the final query will be

dt3.jpg

The same way we can use the substitute variable to define complete query dynamically and the query_text can be constructed conditionally within beforeReportTrigger.

<sqlStatement name="Q1">
<![CDATA[ SELECT &query_text ]]>
</sqlStatement>

Lexical variable can be use in similar way to define a dynamic query. This is EBS/Oracle Report concept to use FND Flex APIs to implement KFF within Data Template.

Here is sample implementation for one of the EBS GL report.

dt4.jpg

<sqlStatement name="Q_MAIN">
<![CDATA[
SELECT
S.user_je_source_name Source,
B.name Batch_Name,
B.default_effective_date Batch_Eff_date,
B.posted_date Batch_Posted_Date,
B.je_batch_id Batch_Id,
B.running_total_accounted_dr B_TOT_DR,
B.running_total_accounted_cr B_TOT_CR,
D.je_header_id Header_id,
D.name Header_Name,
C.user_je_category_name Category,
D.running_total_accounted_dr H_TOT_DR,
D.running_total_accounted_cr H_TOT_CR,
J.je_line_num Je_Line_Num,
decode(nvl(CC.code_combination_id, -1), -1, 'A',null)
FLEXDATA_H,
J.effective_date Line_Effective_Date,
J.description Line_Description,
J.accounted_dr Line_Acc_Dr,
J.accounted_cr Line_Acc_Cr,
D.currency_code Currency_Code,
D.external_reference Header_Reference,
&POSTING_STATUS_SELECT Recerence1_4,
nvl(J.stat_amount,0) Line_Stat_Amount,
GLL.description Batch_Type,
B.actual_flag Actual_Flag,
GLL2.meaning Journal_Type,
SOB.consolidation_sob_flag Cons_Sob_Flag,
&FLEXDATA_DSP FLEXDATA_DSP,
&FLEXDATA_SECURE FLEXDATA_SECURE
FROM
gl_lookups GLL,
gl_je_sources S,
gl_je_categories C,
gl_je_lines J,
gl_code_combinations CC,
gl_je_headers D,
gl_je_batches B,
gl_lookups GLL2,
gl_sets_of_books SOB
WHERE
GLL.lookup_code = B.actual_flag AND
GLL.lookup_type = 'BATCH_TYPE' AND
GLL2.lookup_type = 'AB_JOURNAL_TYPE' AND
GLL2.lookup_code = B.average_journal_flag AND
SOB.set_of_books_id = :P_SET_OF_BOOKS_ID AND
S.je_source_name = D.je_source AND
C.je_category_name = D.je_category AND
J.code_combination_id = CC.code_combination_id(+) AND
J.je_header_id = D.je_header_id AND
&CURRENCY_WHERE AND
D.je_source = NVL(:P_JE_SOURCE_NAME, D.je_source) AND
D.je_batch_id = B.je_batch_id AND
&POSTING_STATUS_WHERE AND
B.name = NVL(:P_BATCH_NAME, B.name) AND
&PERIOD_WHERE AND
B.set_of_books_id = :P_SET_OF_BOOKS_ID
ORDER BY
S.user_je_source_name,
B.actual_flag,
B.name,
B.default_effective_date,
D.name,
J.je_line_num
]]>
</sqlStatement>

Hopefully this will help you to have more control over your data generation process. All the sample templates and PL/SQL package mentioned in this article can be downloaded from following links.
emp_single_query.xml
EMPLOYEEB.pls
GLRGNJ_datatemplate.xml

December 5, 2009

Working with Bursting Listener


While working with bursting functionality, we have complete control over the document generation and delivery process. Bursting controfile provides a high level of flexibility on selecting template, locale, delivery type, delivery destination and output type.

As an intelligent developer, I need further control once the document generation complete. I might want to post process the generated document or supplement some of the missing functionality, which are not available through bursting document generation process. How about to add my customize delivery channel or deliver the document directly to content management system or may be I want to hook my third party application. Before delivery, I might want to validate the document or add some static content to generated PDF document.

So how can we achieve it? Yes, that's correct, we can use BurstingListener interface. Most of us who familiar with bursting functionality, also familiar with BurstingListener interface. But there is limitation with BurstingListener. It does not provide many details like sliced xml data file or document status, which are required to implement my wish list.

There is another listener, oracle.apps.xdo.batch.bursting.BurstingStatusListener. This is available with latest release 10.1.3.4.1 release or EBS 12.03 patch set. It has following method to implement and using these one can code the custom logic as per requirement.

  • beforeProcessDocument(DocumentStatus)
  • afterProcessDocument(DocumentStatus)
  • beforeDocumentDelivery(DocumentStatus status)
  • afterDocumentDelivery(DocumentStatus status)
  • afterEnterpriseDocumentDelivery(DocumentStatus status)

DocumentStatus class provides most of the details required to implement my wish list.
Lets have a look on following sample code, which provides some insight about the BurstingStatusListener usage.

import java.util.Properties;
import java.util.Vector;
import oracle.apps.xdo.batch.BurstingProcessorEngine;
import oracle.apps.xdo.batch.bursting.BurstingStatusListener;
import oracle.apps.xdo.batch.bursting.DocumentStatus;
import oracle.apps.xdo.common.log.Logger;

public class SampleBursting implements BurstingStatusListener
{
public SampleBursting()
{
try {

BurstingProcessorEngine dp = new BurstingProcessorEngine();

dp.deleteTempOutputFile(false);
dp.setTempDirectory("c:\\burst\\sample");//Set the temp file
dp.setXMLAPI("C:\\burst\\sample\\SampleControlFile2.xml"); //Set Bursting Control file.
dp.setData("c:\\burst\\sample\\employee.xml"); //Data File
dp.registerListener(this); //Set the listener

Properties prop = new Properties(); //Add properties and variables//
//Please update these property as per your environment//
prop.put("user-variable:EMAIL_SERVER", "myemailserver.com");
prop.put("user-variable:EMAIL_PORT", "25");
prop.put("user-variable:FROM_EMAIL_ADDRESS", "admin@xyz.com");
prop.put("user-variable:CC_EMAIL_ADDRESS", "cc@xyz.com");
prop.put("user-variable:PRINTER_URL", "ipp://myprintserver:631/printers/printer1");
prop.put("user-variable:EMP_TEMPLATE", "c:\\burst\\sample\\employee.rtf");
dp.setConfig(prop);
dp.process();
//dp.deletTemporaryOutputFiles();
} catch (Exception e) {
Logger.log(e);
}
}

public static void main(String[] args)
{
SampleBursting sampleBursting = new SampleBursting();
}

//Listener Implementation//

public void beforeProcessDocument(DocumentStatus status){}

public void afterProcessDocument(DocumentStatus status){
System.out.println("====afterProcessDocument====");
System.out.println("Document Key:"+status.getKey());
System.out.println("Status:"+status.getStatus());
System.out.println("XML Data File:"+status.getXMLdataFile());
System.out.println("Document:"+status.getOutput());
System.out.println("Delivery Type:"+status.getDeliveryType());
System.out.println("Log:"+status.getLog());

}
public void beforeDocumentDelivery(DocumentStatus status){
System.out.println("====beforeDocumentDelivery====");
System.out.println("Document Key:"+status.getKey());
System.out.println("Status:"+status.getStatus());
System.out.println("XML Data File:"+status.getXMLdataFile());
System.out.println("Document:"+status.getOutput());
System.out.println("Delivery Type:"+status.getDeliveryType());
System.out.println("Log:"+status.getLog());
}
public void afterDocumentDelivery(DocumentStatus status){
System.out.println("====afterDocumentDelivery====");
System.out.println("Document Key:"+status.getKey());
System.out.println("Status:"+status.getStatus());
System.out.println("XML Data File:"+status.getXMLdataFile());
System.out.println("Document:"+status.getOutput());
System.out.println("Delivery Type:"+status.getDeliveryType());
System.out.println("Log:"+status.getLog());
}

public void afterEnterpriseDocumentDelivery(DocumentStatus status){}

}

System log......

====afterProcessDocument====
Document Key:7369
Status:success
XML Data File:c:\burst\sample\120509_114142598\xdo0.tmp
Document:c:\burst\sample\7369_SMITH.pdf
Delivery Type:null
Log:null
====afterDocumentDelivery====
Document Key:7369
Status:success
XML Data File:c:\burst\sample\120509_114142598\xdo0.tmp
Document:c:\burst\sample\7369_SMITH.pdf
Delivery Type:email
Log:null
====afterProcessDocument====
Document Key:7566
Status:success
XML Data File:c:\burst\sample\120509_114142598\xdo4.tmp
Document:c:\burst\sample\7566_JONES.pdf
Delivery Type:null
Log:null
====afterDocumentDelivery====
Document Key:7566
Status:success
XML Data File:c:\burst\sample\120509_114142598\xdo4.tmp
Document:c:\burst\sample\7566_JONES.pdf
Delivery Type:email
Log:null


afterProcessDocument and afterDeliveryDocument triggers provide the xml data file and generated document. These two files are enough to implement any custom logic for post processing. All the information required for post processing could be part of XML data file. We can parse the XML and get the required info for post processing.

Files used in this example are available for download from following location.

SampleBursting.java
SampleControlFile2.xml
employee.xml
employee.rtf


Please try and post your experience. Please feel free to ask your further queries on this topic.


December 3, 2009

Design BIP Report on the fly...

Have you think of creating BIP report dynamically? Data Processor class has many functionalities, which we do not use in general. Lets try some of these functionalities to generate BIP report on the FLY.

DataProcessor class is part of BIP core APIs. Along with data extraction, it also generates RTF layout template, Data Template, XML schema and the BIP report (.xdo) for a given SQL statement or an existing Data Template.

Lets try following peace of code....

public static void bipOnTheFly()
{
String sqlQuery = "select * from emp where deptno=:p_deptno");
Connection connection=null;
try {
connection = getConnection("host:port:sid","userid","passwd");
Hashtable parameter = new Hashtable();

DataProcessor dp = new DataProcessor();
dp.setConnection(connection);
dp.setSql(sqlQuery);
ArrayList paras = dp.getParameters();
Iterator it = paras.iterator();

//Setting parameter value//
while (it.hasNext())
{
Parameter p = (Parameter) it.next();
if (p.getName().equalsIgnoreCase("p_deptno"))
p.setValue("20");
}

//Generate RTF Layout//
dp.writeDefaultLayout("c:\\sample\\employee.rtf");
//Generate XML Schema//
dp.writeXMLSchema("c:\\sample\\employee.xsd");
//Generate BIP Report (.xdo)//
//dp.WriteXDOReport("DataSourceRef","Layout Template","report path","Description","Report Title");
dp.WriteXDOReport("ora10g","employee.rtf","c:\\sample\\employee.xdo","Employee report","Employee Details");

//Generate Sample Data//
dp.setOutput("c:\\sample\\employee_data.xml");

if (paras != null && !paras.isEmpty())
dp.setParameters(paras);

XMLDocument document = (XMLDocument) dp.getDataTemplate();

//Generate data Template//
File outFile = new File("c:\\sample\\employee_dataTemplate.xml");
FileOutputStream outStream = new FileOutputStream(outFile);
document.print(outStream);
dp.processData();

} catch (Exception e)
{ e.printStackTrace(); }
finally
{
try
{ connection.close();}
catch (Exception e)
{ e.printStackTrace(); } } }


The above code will generate RTF template and BIP report. Along with these two components; it also generates sample xml data file, xml schema and Data Template.

RTF Template (Layout)


BIPText1.jpg

BIP Report (.xdo)

 <?xml version = '1.0' encoding = 'UTF-8'?>
 <report xmlns="http://xmlns.oracle.com/oxp/xmlp"
   defaultDataSourceRef="ora10g" version="1.1">
    <title  >Employee Details</title>
    <description  >Employee report</description>
    <properties  />
    <dataModel defaultDataSet="main-query"  >
      <dataSet id="main-query">
         <sql dataSourceRef="ora10g" ><![CDATA[select * from emp 
           where deptno=:p_deptno]]></sql>
         <input id="p_deptno" value="${p_deptno}"/>
       </dataSet>
   </dataModel>
   <parameters  >
      <parameter id="p_deptno" defaultValue="" dataType="varchar2">
         <input label="p_deptno"/>
      </parameter>
   </parameters>
   <templates default="employee"  >
      <template label="employee" type="rtf" url="employee.rtf"/>
   </templates>
</report>

Note: With some of the XDK Version, BIP report might have extra null "xmlns=""" attribute. Please remove these attributes with the help of any text editor.

Upload the BIP Report to BIP server and upload the associated Layout template as well. Make sure "Data Source Ref" defined in the BIP report is present in BIP Server.

BIPText1.jpg


uploadRTF.jpg

Upload the rtf layout template.

BIPText1.jpg

Test the report with View option.

Preview.jpg

Lets try the same for Master->Detail report using SQL Cursor.

Sql Query :

Select deptno,dname,loc,cursor (select * from emp where emp.deptno=dept.deptno)
employee from dept where dept.deptno= nvl(:p_deptno,dept.deptno)

This will generate the RTF Layout with Dept-> Employee master details relationship.


MasterDetailRTF.jpg

Note: There is bug in current code, which mark the detail group as "EMPLOYEE" instead of EMPLOYEE_ROW. Please replace the "EMPLOYEE" with "EMPLOYEE_ROW" before uploading the layout template

FixGroupBug.jpg


Upload the BIP Report and RTF Template to BIP server and test it using View option

MasterDetailReport.jpg

The manual upload could be avoided by using APIs or web services to directly seed the report, this way the whole process could be on the FLY.


November 29, 2009

Having SQL queries from different databases in one data template...

Recently there was question on internal mailing list about the possibilities having SQL queries from different databases in one data template. Yes, it is pretty much possible. Generating report based on the data across multiple DB instances is very common requirement.

In BI Publisher, this functionality termed as Distributed Queries. This allows selecting and merging data across different data sources pointing to different DB types (Oracle, SqlServer …), HTTP, Web Services.

Here is the sample Data Template to extract the data from two different data sources and define a master-detail relationship. The first Data Source (default data source) is based on Oracle and other one specific to Q2 is based on SQL Server.

<?xml version="1.0" encoding="UTF-8" ?>
    <dataTemplate name="data" description="Template description 
        dataSourceRef="oracleDS" version="1.0">
      <parameters>
        <parameter name="P_DeptNo" dataType="number" 
           defaultValue="10"/>
      </parameters>
      <dataQuery>
        <sqlStatement name="Q1"> 
         <![CDATA[SELECT deptno,dname FROM Dept
                  where deptno = :P_DeptNo]]>
        </sqlStatement>
        <sqlStatement name="Q2" dataSourceRef="sqlServerDS" >
           <![CDATA[SELECT DepartmentID,EmployeeID,Title,
                    BirthDate FROM HumanResources.employee
                    where DepartmentID=:deptno ]]>
        </sqlStatement>
       </dataQuery>
       <dataStructure>
          <group name="G_DEPT" source="Q1" >
             <element name="DEPT_NUMBER" value="deptno "  />
             <element name="Name"   value="dname"/>
             <group name="G_EMP" source="Q2">
                <element name="EMPLOYEE_NUMBER" value="EmployeeID" />
                <element name="Title" value="Title"/>
                <element name="BirthDate" value="BirthDate" />
             </group> 
          </group>        
       </dataStructure>
      </dataTemplate>.

Sample java code to execute Distributed query using DataProcessor API.


public static void DistributedQueryTest()

   {
    Connection orclConnection=null;
    Connection sqlConnection=null;
    Hashtable dataSourcRef= new Hashtable();

    try {

    // initialize oracle jdbc connection//
    orclConnection = getOracleConnection(); 

    // initialize sql server jdbc connection//
    sqlConnection = getSQLServerConnection();

    dataSourcRef.put("oracleDS",orclConnection);
    dataSourcRef.put("sqlServerDS",sqlConnection);

    DataProcessor dp = new DataProcessor();
    dp.setDataTemplate("d:\\sample\\DistrubutedlinkQuery.xml");
    dp.setDistributedConnections(dataSourcRef);
    dp.setOutput("d:\\out\\DistrubutedData.xml");
    dp.processData();

    } catch (Exception e)

    {
      e.printStackTrace();
    }
    finally
    {
     try
      {
      orclConnection.close();
      sqlConnection.close();
      } catch (Exception e) 
      {
        e.printStackTrace();
      } } }

June 28, 2009

Bursting & Translation

I see this question very often. Yes, bursting supports the translation and the implementation depends on the flavor. Lets go through the different scenario through these examples

Using bursting control file where layout templates and xlf files are on file system. In this
scenario, set the translation attribute under template section to xlf file path.


<xapi:document key="${EMPNO}"
output="c:\burst\sample\${EMPNO}_${ENAME}.pdf"
output-type="pdf" delivery="123">
<xapi:template type="rtf" locale="de-DE"
location="d:\burst_test\deptSummary.rtf"
filter=".//EMPLOYEE [locale='de-DE']"
translation="d:\burst_test\translation\deptSummary_de_DE.xlf" />
<xapi:template type="rtf" locale="en-US"
location="d:\burst_test\deptSummary.rtf"
filter=".//EMPLOYEE [locale='en-US']"
translation="d:\burst_test\translation\deptSummary_en_US.xlf" />

<xapi:template type="rtf" locale="ja-JP"
location="d:\burst_test\deptSummary.rtf"
filter=".//EMPLOYEE [locale='ja-JP']"
translation="d:\burst_test\translation\deptSummary_ja_JP.xlf" />
</xapi:document>




Bursting-EBS integration where layout template and xlf stored in Template Manager repository. Use the
XDO protocol to define the template location.


<xapi:document key="${TRX_NUMBER}" output="${TRX_NUMBER}"
output-type="pdf" delivery="file1"> <xapi:template type="rtf" locale=""
location="xdo://AR.RAXINV.${language}.${territory}/?getSource=true"
translation="" filter="" />
</xapi:document>


Based on Template code, Application Code, Language and Territory, process extracts the
translated layout from Template Manager repository.

In Enterprise release, Bursting is based on SQL query that we define in Bursting criteria page
of the Report. The Translation is based on the Locale value in SQL Query.

SELECT
CUSTOMER_ID KEY,
CST_TEMPLATE TEMPLATE,
TMPL_TYPE TEMPLATE_FORMAT,
CST_LOCALE LOCALE,
CST_FORMAT OUTPUT_FORMAT,
CST_DEL_CHAN DEL_CHANNEL,
CST_EMAIL PARAMETER1,
'accounts.receivable@oracle.com' PARAMETER2,
'bip-collections@oracle.com'PARAMETER3,
'Your Invoices' PARAMETER4,
'Hi'||CUST_FIRST_NAME||chr(13)|| 'Please find attached your
invoices.' PARAMETER5,
'true' PARAMETER6,
'donotreply@nowhere.com' PARAMETER7
FROM CUSTOMERS

Sample Control files are available here

April 8, 2009

Bursting: Conditional delivery

This is in response to arvind’s question on how we can set the conditional delivery based on the user preference available in xml data file. Here is an example.

<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
 <xapi:globalData location="stream">
 </xapi:globalData >
    <xapi:request select="/DATA/LIST_DEPT/DEPT/LIST_EMPLOYEE/EMPLOYEE">
 <xapi:delivery>
  <xapi:email server="${EMAIL_SERVER}" port="${EMAIL_PORT}"
 from="${FROM_EMAIL_ADDRESS}" 
   reply-to ="${REPL_TO_ADDRESS}">
   <xapi:message id="email1"  to="${TO_EMAIL_ADDRESS}"  cc="${CC_EMAIL_ADDRESS}"   
 attachment="true" content-type="html/text" 
subject="Employee Report for Empno ${EMPNO}">
   Dear ${ENAME}, Please review the attached document.</xapi:message>
  </xapi:email>
  <xapi:filesystem id="file1"
  output="C:\burst\sample\${EMPNO}_${ENAME}_${MGR}_PRINT.pdf"/>
  <xapi:print id="print1"  printer="${PRINT_URL}" copies="2"
   orientation-requested="3" />
 </xapi:delivery>
 <xapi:document key="${EMPNO}" 
  output-type="pdf" delivery="email1">
    <xapi:template type="rtf" location="${TEMPLATE_LOC}"
    filter=".//EMPLOYEE[DEL_CHANNEL='EMAIL']" >
    </xapi:template>
  </xapi:document>
  <xapi:document key="${EMPNO}"   output-type="pdf" delivery="print1">
    <xapi:template type="rtf" location="${TEMPLATE_LOC}"
    filter=".//EMPLOYEE[DEL_CHANNEL='PRINT']" >
    </xapi:template>
  </xapi:document>
 <xapi:document key="${EMPNO}"   output-type="pdf" delivery="file1">
    <xapi:template type="rtf" location="${TEMPLATE_LOC}"
    filter=".//EMPLOYEE[DEL_CHANNEL='FILE_SYSTEM']" >
    </xapi:template>
  </xapi:document>
 </xapi:request>
</xapi:requestset>  

We need to define multiple document sections corresponding to each delivery channel and the filter condition to deliver the output based on user preference. Here DEL_CHANNEL is the element in xml data file, which define the user delivery preference.

March 11, 2009

Bursting...

During these years with Publisher, I worked with many clients directly/indirectly to resolve there reporting issues and one of the very common requirment is about generate mutilple outputs against the single process/report. Here I am sharing one such requirment...

The company’s billing system generates around 30 thousands invoices/bills for each billing cycle. Each billing cycle correspond to a single process and generate a huge set of data for all the invoices.

The process, further consume this huge data and generate the individual Invoices. i.e. one report and multiple output.

The individual Invoice layout depends upon the recipient’s country and local preference, So it could be multiple templates and multiple local. .

The process should take care of document delivery as well. Delivery type and the destination depend on individual client’s preference. Beside it, a copy of each invoice should be save to specific file location or transferred to content management system. To achieve this, the process should allow to hook custom APIs/Logic before and after individual documents generation.

Beside the individual output, there should be another single consolidated output against the same set of data. i.e. a summary report.

So what do you think, Can we satisfy this requirement against the single process?

Yes, The Bursting take care of such type of requirements where single dataset, multiple conditional layouts/templates, multiple layout, multiple delivery and the listener to hook the custom logic at each stage of process.

There are two type of bursting implementation within BI Publisher. The Control File based bursting, which is well integrated with EBS and JDE and available through Public APIs as well. The other one is Delivery SQL based bursting, which is part of Enterprise Release. We will discuss the first one on this post.

The Bursting Control file holds the Meta Data or the bursting rules in XML format.Lets have a look on following control file and see, how far it satisfies the above requirement.

<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
 <xapi:globalData location="stream">
 </xapi:globalData >
    <xapi:request select="/DATA/LIST_DEPT/DEPT/LIST_EMPLOYEE/EMPLOYEE">
 <xapi:delivery>
  <xapi:email server="${EMAIL_SERVER}" port="${EMAIL_PORT}" from="${FROM_EMAIL_ADDRESS}"
    reply-to ="${REPL_TO_ADDRESS}">
 <xapi:message id="123"  to="${TO_EMAIL_ADDRESS}" cc="${CC_EMAIL_ADDRESS}" 
 attachment="true"  content-type="html/text" 
 subject="Employee Report for Empno ${EMPNO}">  Dear ${ENAME}, 
    Please review the attached document.</xapi:message>
  </xapi:email>
  <xapi:print id="printer1"  printer="${PRINT_URL}" copies="2"
   orientation-requested="3" />
 </xapi:delivery>
 <xapi:document key="${EMPNO}"  output="c:\burst\sample\${EMPNO}_${ENAME}.pdf" 
  output-type="pdf" delivery="123">
    <xapi:template type="rtf" location="${EMP_TEMPLATE}">
    </xapi:template>
  </xapi:document>
 </xapi:request>
</xapi:requestset>
A sample control file available here.

This will not exactly satisfy the above requirement but it gives an idea, how we can proceed to achieve the above requirement.

Here is the sample usage of Bursting Engine. We can call bursting engine either directly or through DocumentPrecoessor. Calling directly gives more control over some of the internal methods. API accept the Control file, Data File and Temporary directory path.

Using BurstingProcessorEngine directly..

try {
BurstingProcessorEngine dp = new BurstingProcessorEngine();
dp.deleteTempOutputFile(false);
dp.setTempDirectory("c:\\burst\\sample");//Set the temp file
dp.setXMLAPI("C:\\burst\\sample\\SampleControlFile.xml"); //Set Bursting Control file.
dp.setData("c:\\burst\\sample\\employee.xml"); //Data File
dp.registerListener(this); //Set the listener
Properties prop = new Properties(); //add properties and variables//
prop.put("user-variable:EMAIL_SERVER", "myemailserver.com");
prop.put("user-variable:EMAIL_PORT", "25");
prop.put("user-variable:FROM_EMAIL_ADDRESS", “admin@xyz.com");
prop.put("user-variable:CC_EMAIL_ADDRESS", "cc@xyz.com");
prop.put("user-variable:PRINTER_URL", "ipp://myprintserver:631/printers/printer1");
prop.put("user-variable:EMP_TEMPLATE", "c:\\burst\\sample\\employee.rtf");
dp.setConfig(prop);
dp.process();
dp.deletTemporaryOutputFiles();
} catch (Exception e) {
Logger.log(e);
}

Using DocumentProcessor:

try {
DocumentProcessor dp = new DocumentProcessor("C:\\burst\\sample\\SampleControlFile.xml","c:\\burst\\sample\\employee.xml", "c:\\burst\\sample");
dp.setTempDirectory("c:\\burst\\sample");//Set the temp file
dp.setXMLAPI("C:\\burst\\sample\\SampleControlFile.xml"); //Set Bursting Control file.
dp.setData("c:\\burst\\sample\\employee.xml"); //Data File
dp.registerListener(this); //Set the listener
Properties prop = new Properties(); //add properties and variables//
prop.put("user-variable:EMAIL_SERVER", "myemailserver.com");
prop.put("user-variable:EMAIL_PORT", "25");
prop.put("user-variable:FROM_EMAIL_ADDRESS", “admin@xyz.com");
prop.put("user-variable:CC_EMAIL_ADDRESS", "cc@xyz.com");
prop.put("user-variable:PRINTER_URL", "ipp://myprintserver:631/printers/printer1");
prop.put("user-variable:EMP_TEMPLATE", "c:\\burst\\sample\\employee.rtf");
dp.setConfig(prop);
dp.process();
} catch (Exception e) {
Logger.log(e);
}


A sample test case including sample java application, control file, data file, rtf template is available here. Please make sure you have following libraries in class path
Collection.jar, xdo.jar, Xmlparserv2.jar, mail.jar, Activaton.jar, aolj.jar, i18n, xdoparser.jar,Versioninfo.jar and use 10.1.3.4+ release.

Try it out and let me know your experience. We will see some more interesting example during coming post, so stay tune…

March 2, 2009

My journey with BI Publisher...

It was just last week when I moved out from BI Publisher development and join the Fusion Financial Application development group. All these years working for a great product and with a great team, it was a wonderful experience.

My association with BI Publisher is more then six years old. It was the time, when I was working as a part of Oracle Financial Globalization team on EBS Reporting Strategy project with Tim Dexter. We were trying to create Java applet based Layout Editor to modify the Oracle Report Layout without changing the underlying Data Model. This was the requirement from most of EBS customers for simple customization of report layout. Oracle Report Designer was/is available but it was hard for PMs to use it. During the same period, ATG Internationalization team came up with some exciting prototypes on RTF templates. I still remember about very first POC from Internationalization team on RTF template with one single Form field and the PDF output. That was so exciting that after the demo we dumped the Report Layout Editor project. It was decided to work together to explore further on the same line. Later the product called XML Publisher. First Tim Dexter moved to XML Publisher and subsequently I joined the team as well.

After joining, I start working on XML Publisher integration with EBS. The first integration between Publisher and EBS was through 2 steps Java Concurrent Program to submit a XML Publisher report through EBS. XDO Common Region was another integration with EBS, where XML Publisher report can be embedded within OAF pages.

During up-taking the Publisher within EBS, one of the critical questions was, what we are going to do with 3000 existing EBS reports. Are we going to re-write all these reports? The answer was RDF to RTF migration utility to migrate the RDF Layout.

Data Template (Data Engine) was another exciting contribution to the product. After migrating RDF layout section to RTF Templates, the existing Oracle Reports was still in use to generate the XML Data. It was like two reporting solution for a single report. we came up with the Data Template concept. The very first Data Engine was written in PLSQL and later we migrate it to Java. The Data Template functionality helped us to come up with a complete RDF to XML publisher migration utility. EBS teams has successfully migrated around 2000 Oracle Reports to BI Publisher report with the help of these utilities.

Bursting is one of my most lovable babies. Bursting Engine is very dynamic and supports a wide range of bursting requirement. It has two flavors. Control file based bursting, which is well integrated with EBS and JDE, is much more powerful compare to Delivery SQL based bursting supported through Enterprise Release.

Moving forward, I look after Scheduling functionality of Enterprise Release. I did contribute to enhance this area, but frankly speaking, I don’t see any major contribution over that area.

Here it comes Fusion, It was time again to work closely with Oracle Application Teams. I always feel pleasure while working with developers, customers.

While I moved out last week from the BI Publisher development, I still can’t leave the product. It is too close to my heart and the best way to be in touch with the product, I decided to share my knowledge with the community though this media.

Most of my post will be specific to developer’s point of view. I will start with one of my favorite features Bursting. I feel its is under-documented and the feature is most widely used by the developers community. So stay tune and wait for next post.