Tuesday, April 24, 2012

Launching XML Publisher from OA Framework

Today's example shows how to generate an XML Publisher report from OA Framework. It uses the delivered DocumentViewer bean to invoke an XML Pub report by clicking a button, instead of running a Concurrent Manager program. Rather than just create a "Hello, world" example, we'll embed our custom report in a SSHR function to show how to feed in a parameter from the page.
Lettuce use the Personal Information screen in SSHR to add a button where an employee can generate an Employment Verification letter. I'm a techie and not a generalist so this might not be a great real-world HR example but it is meant to showcase embedding an XML Publisher report inside OAF.
First, lettuce create an XML Publisher report that contains an employee's basic person, assignment and salary information. I like to avoid Oracle Reports altogether so I build my own Data Definition as follows:

Save the above as an .xml file and upload it as an XML Publisher Data Definition by browsing to the following:
(N) XML Publisher -> Data Definitions -> Create
Configure as follows, browsing for your newly created .xml file:

While the point of this exercise is to create and XML Publisher report launched from OA Framework and not from Concurrent Manager, I will also create a Concurrent Program to demonstrate that the report can be launched from two places in Oracle Applications. I also will use the generated XML to create the XML Template for the letter. Here is the configuration of my Concurrent Program that calls XDODTEXE:


Prior to running this report we need to create a PLSQL package that will transfer the P_PERSON_ID parameter into the Data Definition. Copy and paste:
create or replace package gvs_emp_verif_pkg as
--
p_person_id number;
--
gc_person_id varchar2(300);
--
function before_report return boolean;
--
end gvs_emp_verif_pkg;
/
show errors;
create or replace package body gvs_emp_verif_pkg as
--
function before_report return boolean is
--
begin
--
gc_person_id := ' and papf.person_id = '||p_person_id;
--
return true;
--
end before_report;
--
end gvs_emp_verif_pkg;
/
show errors;
Compile these as APPS and then run the report...

After running the report you'll save this output as .xml and construct your .rtf template. Only a summary of the steps necessary for merging an .xml file with a template are shown here. For an end-to-end example, see the previous article "Using XML Publisher without Report Builder":
This is the draft template I will load into XML Publisher:

Browse to (N) XML Publisher -> Templates -> Create Template and configure as follows:

Here's how the letter looks when combining the XML with the template.

Our goal is to get this to launch out of an OA Framework page rather than a Concurrent Program. Here are the steps:
1. Create a custom OA Framework page that extends delivered /oracle/apps/xdo/oa/common/webui/DocumentViewerRn.MainRegion
2. Associate a Controller .class and Application Module to this custom page that invokes method "getXMLData"
3. Create an FND Form Function for the custom page
3. Use personalizations to create a button that points at the FND Form Function
One step at a time, lettuce create our page in JDeveloper:

Right-click your MainRN and add a new region. Populate the "Extends" property with the value /oracle/apps/xdo/oa/common/webui/DocumentViewerRn.MainRegion. You will see the region information populate and be grayed-out since it is delivered by Oracle.

Right-click your MainRN and choose Set New Controller so you can write a few lines of Java code that will pass the person id to the report.

If your JDeveloper application runs as slow as mine, leave this simmer for a few minutes while you personalize (configure) a button in Self-Service HR that will invoke this Page and Controller.
(N) Employee Self-Service -> Personal Information

In the above case I created my own custom responsibility, menus and functions. Click the personalize link in the upper right hand corner:

Click the Create icon next to the cell that has the Update button:

Configure as follows:

That configuration will call our custom page and OA Framework code. Since we haven't developed nor deployed that, return to JDeveloper to complete it. Recall that we have already created a page that has just the delivered DocumentViewer bean and we set a custom Controller. Here is how you should write your Controller code:

Here's the text in case you'd like to copy/paste. I removed some white-space so it would fit on the screen print above.
package hack.oracle.apps.per.hack.webui;
import oracle.apps.fnd.common.VersionInfo;
import oracle.apps.fnd.framework.webui.OAControllerImpl;
import oracle.apps.fnd.framework.webui.OAPageContext;
import oracle.apps.fnd.framework.webui.beans.OAWebBean;
import oracle.apps.fnd.framework.OAApplicationModule;
import oracle.jbo.domain.BlobDomain;
import oracle.apps.xdo.oa.common.DocumentHelper;
import java.io.*;
public class gvsDocCO extends OAControllerImpl
{
public static final String RCS_ID="$Header$";
public static final boolean RCS_ID_RECORDED =
VersionInfo.recordClassVersion(RCS_ID, "%packagename%");
/**
* Layout and page setup logic for a region.
* @param pageContext the current OA page context
* @param webBean the web bean corresponding to the region
*/
public void processRequest(OAPageContext pageContext, OAWebBean webBean)
{
super.processRequest(pageContext, webBean);
pageContext.writeDiagnostics(this,"GVS - Version 2.0",3);
String strPersonId = pageContext.getParameter("PersonId");
pageContext.writeDiagnostics(this,"GVS - strPersonId: " + strPersonId,3);
//
OAApplicationModule oaAM = pageContext.getApplicationModule(webBean);
//
pageContext.putParameter("p_DataSource",DocumentHelper.DATA_SOURCE_TYPE_BLOB);
pageContext.putParameter("p_DataSourceCode","GVS_EMP_VERIF");
pageContext.putParameter("p_DataSourceAppsShortName","PER");
pageContext.putParameter("p_TemplateCode","GVS_EMP_VERIF");
pageContext.putParameter("p_TemplateAppsShortName","PER");
pageContext.putParameter("p_Locale","Default");
pageContext.putParameter("p_Locale","English:United States");
pageContext.putParameter("p_OutputType","PDF");
pageContext.putParameter("p_XDORegionHeight","200%");
Serializable[] oaParams = {strPersonId};
BlobDomain result = (BlobDomain)oaAM.invokeMethod("getGVSData",oaParams);
pageContext.putSessionValueDirect("XML_DATA_BLOB", result);
}
public void processFormRequest(OAPageContext pageContext, OAWebBean webBean)
{
super.processFormRequest(pageContext, webBean);
}
}
Notice that I am doing all of the code in the processRequest. This event fires when the page loads as opposed to pushing a button to generate the document. I want to click a button, launch the page and have the document launch automatically. If you would like the document creation to occur only if you press a button on your custom page just move the last two lines into the processFormRequest.
Also notice that I set the default as "PDF" using this line:
pageContext.putParameter("p_OutputType","PDF");
If you include a default output type you will not get a drop-down from Oracle that allows you to choose a different document type. I will show how it looks both ways later. For now, continue your Jdeveloper hacking by creating an Application Module.
Right-click your project and choose New Application Module...

Here's what it looks like in JDeveloper:

And the text for copy/paste purposes:
package hack.oracle.apps.per.hack.server;
import oracle.apps.fnd.framework.server.OAApplicationModuleImpl;
import oracle.apps.fnd.framework.OAException;
import oracle.apps.fnd.framework.server.OADBTransactionImpl;
import oracle.apps.fnd.framework.server.OADBTransaction;
import oracle.jbo.domain.BlobDomain;
import oracle.apps.xdo.oa.util.DataTemplate;
import java.sql.SQLException;
import oracle.apps.xdo.XDOException;
import com.sun.java.util.collections.Hashtable;
public class gvsPubAMImpl extends OAApplicationModuleImpl {
/**This is the default constructor (do not remove)
*/
public gvsPubAMImpl() {
}
/**Sample main for debugging Business Components code using the tester.
*/
public static void main(String[] args) {
launchTester("hack.oracle.apps.per.hack.server", /* package name */
"gvsPubAMLocal" /* Configuration Name */);
}
public BlobDomain getGVSData (String strPersonId)
{
BlobDomain blobDomain = new BlobDomain();
try
{
DataTemplate datatemplate = new DataTemplate(((OADBTransactionImpl)getOADBTransaction()).getAppsContext(), "PER", "GVS_EMP_VERIF");
Hashtable parameters = new Hashtable();
parameters.put("P_PERSON_ID",strPersonId);
datatemplate.setParameters(parameters);
datatemplate.setOutput(blobDomain.getBinaryOutputStream());
datatemplate.processData();
}
catch(SQLException e)
{
throw new OAException("SQL Error=" + e.getMessage(),OAException.ERROR);
}
catch (XDOException e)
{
throw new OAException("XDOException" + e.getMessage(),OAException.ERROR);
}
catch(Exception e)
{
throw new OAException("Exception" + e.getMessage(),OAException.ERROR);
}
return blobDomain;
}
}
Notice the line "new DataTemplate" needs your Data Template code as the second parameter; the first parameter is the application short name, in my case I used "PER" for Human Resources. Also, any parameters can be supplied using a Hashtable. Remember that my Concurrent Program has one parameter defined and I called the token "P_PERSON_ID". This must match here when supplying a parameter to it. That's it as far as coding goes. In order to deploy this to Oracle Applications you must perform the following:
1. Make and Rebuild your project in JDeveloper
2. .zip the project on your computer and FTP it to $JAVA_TOP
3. Unzip the project in $JAVA_TOP
4. Import the custom page into the database
5. Bounce apache and OA Core
There are two different ways to import your custom page. One is to use jpximport on your machine. The other is to use the XML Importer on the middle tier. Both work fine; I used the jpximport locally using this command:
C:\JDEVOAFR12\jdevbin\oaext\bin\import C:\JDEVOAFR12\jdevhome\jdev\myclasses\hack\oracle\apps\per\hack\webui\gvsPubPG.xml -rootdir C:\JDEVOAFR12\jdevhome\jdev\myclasses -mmddir "C:\JDEVOAFR12\jdevbin\oaext\config\mmd" -username "apps" -password "password123" -dbconnection "(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=test.hack.com)(PORT=1521))(CONNECT_DATA=(SID=HACK)))"
Once you have completed steps 1-5, log in and press the button you configured earlier:

If you press the Export button in the above picture you can download the file:

Recall that I defaulted the document to open automatically and specified PDF. If you comment out this line:
pageContext.putParameter("p_OutputType","PDF");
You will have the choice to choose HTML, PDF, Excel or RTF:

What's it look like in HTML?

Option to download RTF:

Excel: