Monday, June 2, 2008

BI Publisher: BIP in the OA Framework part II

In the last article BI Publisher: BIP in the OA Framework part I we covered how the PO communicate process works. Going forward I will be explaining a project that I wrote that will allow developers to see how all the pieces of the puzzle come together. This project leverages the BIPubliserIDE code. It's been modified to be put into a 11.5.10 cu2 environment, there is a seperate download for this code see BIPublisherIDE_OAF (it's meant to run in OA JDeveloper9i, there is sax issue I'm working around). The classes for this project were refactored but there are very little changes except for coding for AppsContect and OAPageContext. There are some real advantages of doing all your development with BIPublisherIDE. Note: You will probably want to refactor the classes for your company.

As always you are allowed to download this code, modify this code, etc. but you cannot sell it or copyright it for commercial purposes. It's free for everyone to use.

Also, I might generate an avi video that shows the program running and explain a little more about what's going on so you can get this code up and running. That will likely take place in another post...sorry

The Environment: JDeveloper

The main requirement for to run this BIPublisherIDE_OAF locally is that you need to grab all of the classes for $JAVA_TOP/oracle/* or you can use the apps.zip. I prefer to use the classes over the apps.zip, the apps.zip seems to really slow down JDeveloper9i. If you don't have these classes you won't be able to compile the project.

There is a gotcha too in the aol.jar file in the JDeveloper9i. You will need to use this i18nAPI_v3.jar. For more info on this issue, you can read up on it in this post in OAF forum. It's very likely you will also need to remove these classes from the aol.jar file oracle.apps.fnd.common.* (only in JDeveloper9i)

The following jar files are not included in the project and you will have to download them. The following files are needed in order to run the project (see BIP library):

1. j5472959_xdo.zip
2. collections.jar
3. versioninfo.jar
4. i18nAPI_v3.jar
5. xdoparser.jar
6. xmlparserv2-904.jar

*Files 2-6 can be found under $JAVA_TOP/oracle, Item 1. you will have to download the patch

The Environment: (linux, unix or windows)

A lot of companies get "fussy" and there is a generally misunderstanding that you have to put java classes in the oracle's code tree. This is simply not true.

This is not a mandatory step or requirement to understanding the OA Framwork or Java Concurrent Programs, but generally speaking this is a best practice. If your doing java development in the EBS you might not have your environment setup correctly or at all. Here's what you should have setup:

Under YOUR custom top create the following directory structure for your customizations: $XX_TOP/java/oracle/apps/[your_top_here]

Note this is not to be confused with $JAVA_TOP. This is the code tree for your companies customizations. You will also need to add this as the VERY FIRST classpath in the jserv.properties file in apache and do a hardbounce. This will not only allow your classes to be loaded but it will also allow you to override oracles classes without removing/overwriting them in $JAVA_TOP.

How It Works

The design for the BIPublisherIDE_OAF is to allow developer to have a standardized way of interacting with the BIP Api's seemsly despite what environment there in. There are two ways of running data templates and format templates using the BIP Api's in the EBS:

1. XML Publisher Repository (EBS environment)
2. Flat-files (Non-EBS environments or Development)

The solution a developer should use for the OAF is pulling the data template and format template from the XML Publisher Repository. The two oracle bip classes to help you achieve this are:

1. Data Templates: DataTemplate.class
2. Format Templates: TemplateHelper.class

The BIPublisherIDE_OAF code has done all of the wrapping around these classes. A developer doesn't really have to worry about much except for a basic understanding of the OA Framework. In order to get a report to display in a browser the developer has to code the following:

1. have the application short name for the format template
2. have the code (name) for the format template
3. have the application short name for the data template
4. have the code (name) for the data template
5. set the parameters. note the code is designed for the OAF piece to map the session parameters to BIP parameters for the data template. This is unnecessary for java concurrent programs, because the parameter are in hashTable already.
6. Output type (PDF, EXCEL, HTML, RTF) (this is done in XMPublisherAPI constructor)
7. generate the xml
8. apply the template
9. display the document in the OAF.

Here's the sample code. Pretty basic. You will need to extend the XMLPublisherAPI, call the superclass and everything is good to go. All you have to do is call the api's for format and data templates.

You'll also notice in the snippet below that an assumption is not made that the data template code and format template code match.

package oracle.apps.xbol.ipt.webui;

import oracle.apps.fnd.common.AppsContext;
import com.sun.java.util.collections.Hashtable;
import oracle.apps.fnd.framework.webui.OAPageContext;
import oracle.apps.fnd.framework.OAException;
import oracle.apps.fnd.common.EnvironmentStore;
import java.util.Enumeration;
import java.io.File;
//import java.util.Random;
import oracle.apps.fnd.common.VersionInfo;
import oracle.apps.xbol.bipublisher.api.XMLPublisherApi;
import oracle.apps.fnd.framework.webui.beans.OAWebBean;

public class IPTInvoiceGenerator extends XMLPublisherApi
{
static oracle.apps.xdo.common.log.Logger logger;
public IPTInvoiceGenerator (OAPageContext pageContext, AppsContext appsContext, OAWebBean webBean)
{
//output type is mandatory!!!!!
super(pageContext, appsContext, webBean, pageContext.getParameter("output"), getOALookup());
StringBuffer sb = new StringBuffer();
String xdoTempCode ="";
String xdoTempShort ="";
String xdoDataCode ="";
String xdoDataShort ="";
try
{
setStatus("SUCCESS");
//int i = System.getProperty("os.name").indexOf("Linux");
//set the org if were in linux....
if (System.getProperty("os.name").indexOf("Windows") == -1 )
{
String orgId = pageContext.getParameter("orgId");
setOrgContext(orgId);
}

logger.getLog().setLevel(logger.STATEMENT);
setOutputDirectory(getFileDirectory(pageContext));
setOutputFile("COSTPLUS_"+getRequestId());

//setup data template code
if (System.getProperty("os.name").indexOf("Windows") == -1 )
{
String orgId = pageContext.getParameter("orgId");
setOrgContext(orgId);
xdoTempCode = pageContext.getParameter("template");
xdoTempShort = pageContext.getParameter("tempShort");
xdoDataCode = pageContext.getParameter("dataTemplate");
xdoDataShort = pageContext.getParameter("dataShort");
} else
{
//hard-coding for windows...See screen shots of BIP config to make sense of
//whats going on
xdoTempCode = "XXPO_INT_INVOICE";
xdoTempShort = "XBOL";
xdoDataCode = "XXPO_INT_INVOICE";
xdoDataShort = "XBOL";
}
if (xdoTempCode == null)
{
throw new OAException("IPTInvoiceGenerator- Template code not provided", OAException.ERROR);
}
if (xdoTempShort == null)
{
throw new OAException("IPTInvoiceGenerator- Template short name not provided", OAException.ERROR);
}
if (xdoDataCode == null)
{
throw new OAException("IPTInvoiceGenerator- Data Template code not provided", OAException.ERROR);
}
if (xdoDataShort == null)
{
throw new OAException("IPTInvoiceGenerator- Data Template short name not provided", OAException.ERROR);
}

//set data template code
, no link is required to the format template
setXdoDataDefinitionCode(xdoDataCode);
setXdoDataDefinitionApplicationShortName(xdoDataShort);

//setup format template code, no link is required to the data template
setXdoTemplateCode(xdoTempCode);
setXdoTemplateApplicationShortName(xdoTempShort);

//if it's windows than use hard-coded hashtable, otherwise parameters are set in super class
if (System.getProperty("os.name").indexOf("Windows") >= 0 )
{
setParameters(getHashtable());
}

//apply the format template
if ("SUCCESS".equalsIgnoreCase(getStatus()))
{
oaDataEngine();
}

if ("FAILURE".equalsIgnoreCase(getStatus()))
{
throw new OAException("IPTInvoiceGenerator- Failed to generate xml file", OAException.ERROR);
}

//apply the format template
if ("SUCCESS".equalsIgnoreCase(getStatus()))
{
applyTemplate();
}

displayOAFDocument(); //pop the document up on the browser

if ("FAILURE".equalsIgnoreCase(getStatus()))
{
throw new OAException("IPTInvoiceGenerator- Failed to apply format template", OAException.ERROR);
}

//dumpProperties(pageContext,appsContext);
}
catch (Exception e)
{
e.printStackTrace();
pageContext.writeDiagnostics(this, "IPTInvoiceGenerator::constructor - error - " + e.getMessage(), 1);
} finally
{
appsContext.releaseJDBCConnection();
}
}


public final static Hashtable getOALookup()
{
Hashtable parameters = new Hashtable();
/**@Mapping:
*=================== OA Parameter | @BIP Parameter (see form function call =============
bi publisher ide will search oa parameters, get there values and map them to the data template parms.
*/
parameters.put("P_MLOGO_ID" , "P_MLOGO_ID");
parameters.put("P_CONC_REQUEST_ID" , "P_CONC_REQUEST_ID");
parameters.put("P_DIR_PROJECT_ID" , "P_DIR_PROJECT_ID");
parameters.put("P_INDIR_PROJECT_ID" , "P_INDIR_PROJECT_ID");
parameters.put("P_INVOICE_NUMBER" , "P_INVOICE_NUMBER");
parameters.put("P_COST_PLUS_BILLING_ID" , "P_COST_PLUS_BILLING_ID");
parameters.put("P_TO_DATE" , "P_TO_DATE");
parameters.put("P_FROM_DATE" , "P_FROM_DATE");
parameters.put("P_ORDER_BY" , "P_ORDER_BY");
parameters.put("P_ORDER_BY_BILL_CODE" , "P_ORDER_BY_BILL_CODE");
parameters.put("orgId" , "P_ORG_ID");
parameters.put("P_DEBUG" , "P_DEBUG");
return parameters;
}

//this is for testing in windows...
public static Hashtable getHashtable()
{
Hashtable parameters = new Hashtable();
parameters.put("P_MLOGO_ID" ,"1");
parameters.put("P_CONC_REQUEST_ID" ,"0");
parameters.put("P_DIR_PROJECT_ID" ,"798");
parameters.put("P_INDIR_PROJECT_ID" ,"");
parameters.put("P_INVOICE_NUMBER" ,"24636");
parameters.put("P_COST_PLUS_BILLING_ID" , "");
parameters.put("P_TO_DATE" ,"");
parameters.put("P_FROM_DATE" ,"");
parameters.put("P_ORDER_BY" ,"D");
parameters.put("P_ORDER_BY_BILL_CODE" , "YES");
parameters.put("P_ORG_ID" , "84");
parameters.put("P_DEBUG" ,"XML");
return parameters;
}


public String nvl(String str, String replacementValue)
{
return str == null || str.equalsIgnoreCase("")? replacementValue: str;
}

public String getFileDirectory(OAPageContext pageContext)
{
StringBuffer path = new StringBuffer();
try
{
String osName = System.getProperty("os.name");
pageContext.writeDiagnostics(this, "IPTInvoiceGenerator::getFileDirectory() resolved operating system - " + osName.toString(), 1);
if (osName != null && osName.indexOf("Windows") >= 0)
{
//files go right to the c: drive
path.append("C:");
path.append(File.separator);
}
else
{
//files go to the tmp directory in unix.
path.append(File.separator);
path.append("tmp");
path.append(File.separator);
}
}
catch (Exception e)
{
pageContext.writeDiagnostics(this, "IPTInvoiceGenerator::getFileDirectory() - " + e.getMessage(), 1);
}
pageContext.writeDiagnostics(this, "IPTInvoiceGenerator::getFileDirectory() resolved file dir - " + path.toString(), 1);
return path.toString();
}


public static final String RCS_ID = "$Header: COSTPlusGenerator.java 115.1 2008/03/27 08:18:27 iwiggins noship $";
public static final boolean RCS_ID_RECORDED = VersionInfo.recordClassVersion("$Header: COSTPlusGenerator.java 115.1 2008/03/27 08:18:27 iwiggins noship $", "oracle.apps.xbol.costplus.webui");
}


The design to associate a format template with and data template is intentional. This will allow a developer to link up any format template with any data template and this is the way it should work. Going forward oracle fusion publisher will probably work the same way which is great!

See Tim Dexter's blog: http://blogs.oracle.com/xmlpublisher/2008/06/02#a986

So now that we have the code, how do we call this from a form? It's really simple...We have to call an OAF page and the controller class has to run something similar to the snippet above. Now your probably wondering how do I call an oaf page from forms? We need to leverage fnd_function.execute and call a menu function for the OA page and the rest is history.

Here's a snippet of how in your form:

--NOTICE THESE MAP OUT IN THE JAVA SNIPPET ABOVE (see getOALookup())
--For the sake of consistency I named the parameters the same for oaf and the data template.

--you can't see it but this form actually supports 3 similar reports!!!!
fnd_function.execute(
function_name => 'XX_VIEW_IPT_DOCUMENT',
other_params=>'P_DIR_PROJECT_ID='||:BILL_ITEMS.PROJECT_ID||
--'&P_INVOICE_NUMBER='||:BILL_ITEMS.INVOICE_NUMBER||
'&orgId='||:BILL_ITEMS.ORG_ID||
'&output='||:CTRL_BLOCK.XDO_OUTPUT_TYPE||
'&template='||:CTRL_BLOCK.TEMPLATE_CODE||
'&tempShort='||:CTRL_BLOCK.TEMPLATE_APP_SHORT_NAME||
'&dataTemplate='||:CTRL_BLOCK.DATA_SOURCE_CODE||
'&dataShort='||:CTRL_BLOCK.DATA_TEMPLATE_APP_SHORT_NAME||
'&P_DEBUG=XML'||
'&P_COST_PLUS_BILLING_ID='||:BILL_ITEMS.COST_PLUS_BILLING_ID||
'&P_ODRDER_BY_BILL_CODE=YES'||
'&P_ORDER_BY=D'
);


Now to fill in some of the gaps, below is a highlevel diagram, the form and bip config. You may need to click on the image to see the whole picture.

Free Image Hosting

Notice that BIP code, data template and format templates can now be leveraged with a concurrent request or in forms6i!!!!

Free Image Hosting

Have to make it sswa!


Free Image Hosting

Wow, are there two oaf pages here, does the other page pop up another report? Yep it sure does. It works really slick, users never have to key a single parameter and they can also run a series of reports right from the form!

Free Image Hosting

Free Image Hosting

Notice the data template! If you still creating xml from reports6i, you should really stop doing this, your coding yourself in a corner!

Free Image Hosting


BI Publisher: xdo.cfg & BIPublisherIDE

For those of you out there that have downloaded the BIPublisherIDE a question may be plaguing you: "How do I use the xdo.cfg or xdodebug.cfg file, where do I put those suckers?"

After some digging today I helped out a soon to be poster on this blog, Brett Baldwin. Here's how you dig into this problem.

Step 1: Run the BIPublisherIDE
Step 2: Pause it in JDeveloper and look at the log for the following text:

Note: Directory structures will differ based on your versions of jre or drive letters

[060208_051345725][][STATEMENT] XDO version = Oracle XML Publisher 5.6.3
[060208_051345725][][STATEMENT] java.home = C:\Program Files\Java\jre1.6.0_06
[060208_051345725][][STATEMENT] XDO_TOP = null
[060208_051345725][][STATEMENT] Config Path = null
[060208_051345725][][STATEMENT] Debug Cfg Path= null
[060208_051345740][][STATEMENT] Font dir = C:\Program Files\Java\jre1.6.0_06\lib\fonts\
[060208_051345740][][STATEMENT] Locale = en-US
[060208_051345740][][STATEMENT] Fallback font = type1.Helvetica

Step 3: Find the java home, see above and blue and create two files in the lib directory for our example it would the following:

C:\Program Files\Java\jre1.6.0_06\lib\xdo.cfg
C:\Program Files\Java\jre1.6.0_06\lib\xdodebug.cfg

Step 4: Add whatever additions you need to these files, for xdo.cfg this is particularly useful for fonts (barcodes), currencies, etc (see oracle's bip manual). For xdodebug, you can use the xdodebug.cfg (really not necessary client side, however very useful server-side and for oaf customizations.

Step 5: Re-run or click debug, You should see the following the configs are now being picked up!

[060208_052916553][][STATEMENT] [ PDF GENERATOR ]---------------------------------------------
[060208_052916553][][STATEMENT] XDO version = Oracle XML Publisher 5.6.3
[060208_052916553][][STATEMENT] java.home = C:\Program Files\Java\jre1.6.0_06
[060208_052916553][][STATEMENT] XDO_TOP = null
[060208_052916553][][STATEMENT] Config Path = C:\Program Files\Java\jre1.6.0_06\lib\xdo.cfg
[060208_052916553][][STATEMENT] Debug Cfg Path= C:\Program Files\Java\jre1.6.0_06\lib\xdodebug.cfg
[060208_052916553][][STATEMENT] Font dir = C:\Program Files\Java\jre1.6.0_06\lib\fonts\
[060208_052916553][][STATEMENT] Locale = en-US
[060208_052916553][][STATEMENT] Fallback font = type1.Helvetica


Hope this was helpful, Keep on Bipin!