Main

Data Extraction Archives

April 24, 2006

Hotels Nashville style and how get your external data ...

Collaborate06 has started with a bang ... this hotel is huge, just to give you an idea how big ... they have a river running through the grounds of the hotel (all of which are under glass) and you can ride a boat and get a half hour guided tour of the hotel. The fish are huge, wish I had brought my rod!

Monday is pretty much given over to Special Interest Groups during the day. This evening the exhibition floor opened to floods of attendees with burning questions. We have an Applications Technology pod staffed by yours truly and a few other brave souls. It was pretty busy ... not OpenWorld busy but busy enough.


Adding External Data


I got a request tonight; 'How can I bring other data into the layout that is not in the datasource provided?' ... there are a couple of ways to do this. Now Im not going to tell you how to execute a query from the template to pull in more data from the db ... that starts to tie the template to the data extract too closely and we go down a path we do not want to go ... that said its possible but we're not recommending it. If you have some static data or can call a URL to pull XML data; maybe a servlet or static XML document then you can easily integrate this into you output.


There is a function in XSL called document(), we can use this to pull XML data into the template very easily. Basically we declare the function at the top of the template. For this example lets use a well known RSS news feed.

<xsl:variable name="news" select="document('http://rss.news.yahoo.com/rss/topstories')"/>

We create a variable, 'news'  and assign the RSS feed results to it using the document function. The variable will contain a nodeset or tree of data not just a single element.

<?xml version="1.0" encoding="iso-8859-1" ?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss">
<channel>
<title>Yahoo! News: Top Stories</title>
<copyright>Copyright (c) 2006 Yahoo! Inc. All rights reserved.</copyright>
<link>http://news.yahoo.com/i/716</link>
<description>Top Stories</description>
<language>en-us</language>
<lastBuildDate>Tue, 25 Apr 2006 06:18:26 GMT</lastBuildDate>
<ttl>5</ttl>
<image> <title>Yahoo! News</title>
...

We can now reference the "news" variable and its contents in the template.


ExternalXML:


The image shows a portion of a template using the "news" variable.


FE - <?for-each:$news/*?> - this is pulling the complete nodeset into the for-each loop
Channel Link - <?/rss/channel/title?> simply referencing the title element
Link - this is a piece of text i.e. 'Link' with an MSWord hyperlink layered over the top,{/rss/channel/link}  - I'll cover the whole notion of links and dynamic links in another post.
FE - <?for-each:item?> - now we start looping over the news item data just as we would normally.
The rest you can check out yourselves, the template is posted here. When you test remember you may need to set a proxy to get out of your firewall.



So pretty simple huh? Of course you can get a lot more imaginative, say your XML source accepted parameters you could pass parameter values based on the main data set, you can build dynamic URLs to open another report, even a self service web page or an Oracle Form or anything else for that matter.
Busy day tomorrow with panels, presentations and the demogrounds ... see ya!


 

May 5, 2006

Using SQL XML with the XMLP data engine ...

Those of you that have used the data extraction engine will know that it can generate structured XML when executing a data template, with all the bells and whistles, event triggers, group filters, summary columns, etc. The engine can also accept a simple sql statement too including parameters. Passing say:


select JOB,
         ENAME,
         DEPTNO 
 from EMP


Will render the following XML.
<?xml version="1.0" encoding="UTF-8" ?>
<ROWSET>
 <ROW>
  <JOB>CLERK</JOB>
  <ENAME>SMITH</ENAME>
  <DEPTNO>20</DEPTNO>
 </ROW>
 <ROW>
  <JOB>SALESMAN</JOB>
  <ENAME>ALLEN</ENAME>
  <DEPTNO>30</DEPTNO>
 </ROW>
 <ROW>
  <JOB>SALESMAN</JOB>
  <ENAME>WARD</ENAME>
  <DEPTNO>30</DEPTNO>
 </ROW>
</ROWSET>
Thats all very nice but what if you wanted to extract the data in a structured format with out having to develop a data template. Well now you can, the latest release of the engine supports 
SQL XML. This sql format allows you to extract hierarchical XML from the Oracle database, the XMLP data engine can now support this too.
So the following statement:

SELECT XMLELEMENT("Department", XMLAttributes(deptno AS "deptno"),
                  XMLAgg(XMLElement("Employee", e.job||' '||e.ename))).
getClobVal()
   AS "Dept_list"
   FROM emp e
   GROUP BY e.deptno


will generate the following XML:
<?xml version="1.0" encoding="UTF-8" ?>
<ROWSET>
 <ROW>
  <Dept_list>
   <Department deptno="10">
    <Employee>MANAGER CLARK</Employee>
    <Employee>PRESIDENT KING</Employee>
    <Employee>CLERK MILLER</Employee>
   </Department>
  </Dept_list>
  </ROW>
  <ROW>
   <Dept_list>
    <Department deptno="20">
     <Employee>CLERK SMITH</Employee>
     <Employee>ANALYST FORD</Employee>
     <Employee>CLERK ADAMS</Employee>
     <Employee>ANALYST SCOTT</Employee>
     <Employee>MANAGER JONES</Employee>
    </Department>
   </Dept_list>
  </ROW>
...
</ROWSET>


Notice, still rowset/row but there is now heirarchy in the sub elements ... no more regrouping for me in my template :o)
For the XMLP engine to sucessfully extract the data using the SQL XML format we need to append the .getClobVal() function to the end of the statement.
So, the world is your oyster when it comes to using SQL with the XMLP data engine .. so start shucking:o)

July 21, 2006

Correcting XML Date Formats from a VO object

Finally, an EBS posting!


For those of you using XMLP in your OA Framework pages you may have noticed (as did one of our EBS developers) that the VO method writeXML is great but the dates are pushed out in a non XMLP format i.e. 2006-06-05 07:01:12.0. XMLP needs to have the XSD date format,'YYYY-MM-DDTHH:MM:SS' to allow you to format the date correctly in the layout template.


To get this format Steve Meunch came to the rescue, thanks Steve.


Customizing the getter method of the view object's row class to return a Date with a custom implementation of the
getXMLContentNode() method, using whatever format you like for the date value. Will get you the correct date format.

For example, if you created an EmpView having Empno, Ename, and Hiredate attributes, your EmpViewRowImpl.java class could overide the getter method for the Hiredate attribute like this:

Change:

 public Date getHiredate() {
   return (Date)getAttributeInternal(HIREDATE);
}

To this:

 public Date getHiredate() {
   return new Date((Date)getAttributeInternal(HIREDATE)) {
     public Node getXMLContentNode(Document xmlDoc) {
       SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
       return xmlDoc.createTextNode(sdf.format(timestampValue()));
     }    
   };
}

Doing so would coax writeXML() to change its output from:

<EmpView>
  <EmpViewRow>
     <Empno>7369</Empno>
     <Ename>SMITH2</Ename>
     <Hiredate>2006-07-21 10:50:32.0</Hiredate>
  </EmpViewRow>
</EmpView>


To this:

<EmpView>
  <EmpViewRow>
     <Empno>7369</Empno>
     <Ename>SMITH2</Ename>
     <Hiredate>2006-07-21T10:50:32</Hiredate>
  </EmpViewRow>
</EmpView>

November 1, 2006

Web Service Datasources for XMLP Enterprise

A recent enquiry about web service support for Enterprise has promted me to spend some time writing up how to do it here ... our docs are somewhat lacking :o( we'll address that in the next release.
Firstly, it can be done, secondly its pretty simple once you understand the limitations and the of course what the WSDL behind the service can actually do.


Im going to use the service described here, http://www.webservicex.net/stockquote.asmx?WSDL. In the WSDL there is a getQuote method that we canuse to get stock quote information. It requires of course a quote parameter value such as 'orcl'. Today it will return:-

  <StockQuotes>
 <Stock>
  <Symbol>ORCL</Symbol>
  <Last>18.47</Last>
  <Date>11/1/2006</Date>
  <Time>4:23pm</Time>
  <Change>0.00</Change>
  <Open>18.56</Open>
  <High>18.65</High>
  <Low>18.34</Low>
  <Volume>31724216</Volume>
  <MktCap>95.960B</MktCap>
  <PreviousClose>18.47</PreviousClose>
  <PercentageChange>0.00%</PercentageChange>
  <AnnRange>11.75 - 19.25</AnnRange>
  <Earns>0.666</Earns>
  <P-E>27.73</P-E>
  <Name>ORACLE CORP</Name>
 </Stock>
</StockQuotes>

.....  nice run up Oracle has had recently!

Anyhoo, how do you get this working in Enterprise.
Step 1: Create a new report

So give the report a name, description, etc then add a new Data Model

Step 2: Define the data model 




Give it a name, the type should be set to 'Web Service', the Details section will update for you. now enter:


WSDL URL - http://www.webservicex.net/stockquote.asmx?WSDL
Method - GetQuote

At this point we can not add the parameter, we need to create in the UI first andthen add it later.

Step3: Add the quote parameter
Click on Parameters and click New to create a new parameter.


Enter the following:


Identifer - whatever you wish
Data Type - String
Default Value - if you want
Parameter Type - Text
Display Label - the user will see this so make it something obvious
Text Field Size - I guess 4 is going to be the max you'll need


Step 4: Add the parameter to the Data Model

Save your work and go back to your new Data Model and click on the 'Add' link next to the Parameters label. The drop box will default in the new parameter.

You're done. Now click on the 'View'link and enter a stock quote and hit the 'View' button and the quote XML will appear. Now all you need is a layout template.

Of course you can create multiple data sources adding a query to the web service and then combine the result into a single report. For those of you with Enterprise installed you can get the report definition here so you can load it to your instance. Remember that the URL is going to be outside of your company firewall so you'll want to start the server up with the proxy parameters.

-DproxySet=true -DproxyHost=www-proxy.us.oracle.com -DproxyPort=80

November 8, 2006

Introducing Data Templates

Another series of articles on the way ... this time concerning the relatively new data extraction engine from XML Publisher that is currently available in the Enterprise and E Business Suite flavors.


First a little history ... 


Why build another extraction tool, there are others that can generate XML, Oracle Reports, various solutions from the database folks, the list goes on. To answer why you have to understand the volumes of data we are trying to handle here and the furture of reporting for Oracle Applications. For EBS in particular we had multiple reporting solutions, the majority of which were based on Oracle Reports. The plan going forward is to remove Oracle Reports from the techstack in the fusion timeframe. Therefore we needed an extraction engine that offered all that Oracle Reports offers and was just as fast - Oracle Reports is danged fast at generating XML data. On top of that we are not talking about extracting data for a 2 page invoice, the engine needs to extract millions of rows to build tens of thousands of pages for some reports. 
We looked at what was out there, SQL XML provided fast extraction for large data sets but we needed among others, event triggers and flexfield user exits, plus users would need to learn the wrapper functions used to generate the hierarchies. After a lot of research we decided to build an engine ourselves with all the bells and whistles needed to cover the Oracle Reports replacement requirements.


Features ... features ... features


To dig in a little to the features the engine provides:


Fast, scalable extractions - its fast, faster than Oracle Reports. We worked closely with the Oracle Performance team and had to re-write it a couple of times to get their approval. It needs to be fast too, those of you that use Oracle Reports EBS know that out of the box we generate flat text character based output. With XMLP we're generating highfidelity output ... that requires more processing time so the more time we save on the extraction the more we have for formatting without slowing the report generation.


Oracle Reports Features - if its going to replace OR in EBS then it has to match OR on features:


  • Multiple Queries/Joins -  enabling master/detail extractions
  • Event Triggers - pre- and post- fetch for business rule processing. Currently plsql support but will add java
  • Flexfield Support - gotta get that natural account description with out the select statement - this is an EBS feature.
  • Formula/summary columns -  again similar to Oracle Report functionality allowing you to create aggregation values and pl/sql based formula columns in your extraction
  • Data Structure - this allows you to build a hierarchy into your XML data similar to the grouping abilities in Oracle Reports
  • Group filters - a la Oracle Reports
  • etc


on top of that its also has:




  • Rich Java API layer - call a data template from your jsp or java app
  • Distributed Queries - this is neat. You're not tied to a single db nor even to an Oracle db. You can construct a data template that, for example hits an MSSQL instance for customer data and an Oracle db for their invoices. the engine will generate a single result set of hierarchical XML i.e. Customer1
     Invoice1
     Invoice2
    Customer2
     Invoice1
     Invoice2

  • Static XML File support - query across a db and an XML file. Text and XLS support coming.
  • Pluggable Data Templates - not here yet but theidea here is that you can build a core data template and then allow a plugin DT to be applied over the top to get more data from other sources.

Some of you will have wondered what the heck a Data Template is? this is the name we give to the extraction definition, its an XML representation of the queries, joins, data structure, etc.

Thats the brief overview, next Im going to cover some sample Data templates over the next few articles from the most basic to an all singing all dancing EBS DT, I'll also post a jsp appyou can use to test DTs and some help on how to build them.

November 29, 2006

Data Templates by Example

Apologies for the delay ... I have lots of content and examples for you but I did not have a means for you to test your data templates. I have now rectified that and have taken one of our internal testbeds and packaged it up so you can test to your hearts content. The test bed is a web application that runs inside OC4J, I have not tested it with any other mid tier flavors but if you're game.


Before we start on the content lets install the app and test a sample data template.


  1. Download the EAR file here. Well you would be able to download it if I can find somewhere that can host it. for now drop me a mail and I'll get it to you. 

    Finally found a home for the file on my website, you can get the file here, http://www.banzel.com/download/DTTestBed.ear
     
  2. I have tested with OC4J 9.0.4, 10.1.2 and 10.1.3 so once you have the EAR its a simple case of deploying it. If you do not have OC4J installed, go for the latest 10.1.3 release and have a nice UI to help you deploy the app.
  3. Once installed you just need to point your browser to http://yourserver:port/DTTestBed/DT1.jsp
    4. The first page you'll see is:
    DTTB1:


    I have created a small data template that will run against the scott/tiger schema, so download it from here. Then use the browse button to find it and hit Upload.


  4. You'll then get a page like this:


    DTTB2:

    Heres where the fun starts, so in the page you have a parameter section, these are the parameters parsed out of the data template in this case we only have one, pDeptNo. You can fill in a value or leave it blank to get the complete data set.
    Then we have the runtime options:


    • Generate XML Schema  - fairly obvious but very useful if you are going to be doing some PDF template mapping
    • Generate Default Layout - this will generate an RTF template for you based on the data structure of the data template, again useful to get you started
    • Layout Template - if you have already developed an RTF template (no it does not support PDF templates yet) then you can load it to be applied to the resulting data. If you choose this option then a default template will not be generated.
    • Database - we need a connect string to the database, hostname:port:sid
    • User Name - obvious
    • Password - obvious if you have it
    • ORG ID - this is for EBS customers that want to test data templates running against the EBS db - its not required.


  5. So fill in some values so you have something like this:


    DTTB3:


    and hit Submit
    7. If all went well then you'll get a page of results similar to this:


    DTTB4:


    There will be links to the various objects that XMLP has generated. These are all created in a temp directory under the root of the web app in mycase

    D:Oc4j10.1.3j2eehomeapplicationsDTTestBedDTTestBedtemp
    . Be sure to clean that directory up regularly it will fill your disk all to quickly.


So thats the test bed we are going to use in the coming articles ... get it up and running and you'll be able to try your first data template.  

December 13, 2006

Hello Data Templates

Hopefully you have downloaded and installed the Data Template Test Bed I provided in my last post, you may have even loaded and run the sample I provided ... hopefully sucessfully. You may have even gone off and got started on your own. Well we are going to take a step back here and get back to basics and then move up to the more powerful stuff like filters, triggers and flexfields ... we're going to get there quickly so please be patient.


Being the consumate geek and not wanting to break tradition when it comes to starting out with a new technology we're going to build and test a Hello World data template ... now known as 'DTs' for brevity. First lets take a look at the anatomy of a DT, it'll help later.

<dataTemplate name="EMPLOYEES" defaultPackage="" description="Employee Data">
   <properties>
      <property name="include_parameters" value="true"/>
      <property name="include_null_Element" value="true"/>
      <property name="xml_tag_case" value="upper"/>
      <property name="db_fetch_size" value="500"/>
      <property name="scalable_mode" value="off"/>
      <property name="include_rowsettag" value="false"/>
      <property name="debug_mode" value="off"/>
   </properties>
   <parameters>
      <parameter name="pDeptNo" dataType="number" defaultValue=""/>
   </parameters>
   <lexicals/>
   <dataQuery>
  <sqlStatement name="Q1" dataSourceRef=""><![CDATA[SELECT DEPTNO,DNAME,LOC
from dept where deptno = nvl(:p_DeptNo,deptno)
order by deptno]]>
</sqlStatement>
  <sqlStatement name="Q2" dataSourceRef=""><![CDATA[SELECT  EMPNO,ENAME,JOB
,MGR,HIREDATE,SAL,nvl(COMM,0) COMM 
from EMP
WHERE DEPTNO = :DEPTNO]]>
</sqlStatement>
  </dataQuery>
   <dataStructure>
  <group name="G_DEPT" source="Q1" groupFilter="">
   <element name="DEPT_NUM" value="DEPTNO" function=""/>
   <element name="DEPT_NAME" value="DNAME" function=""/>
   <element name="LOCATION" value="LOC" function=""/>
   <group name="G_EMP" source="Q2" groupFilter="">
    <element name="EMPNO" value="EMP_NUM" function=""/>
    <element name="EMP_NAME" value="ENAME" function=""/>
    <element name="JOB_TITLE" value="JOB" function=""/>
    <element name="MANAGER" value="MGR" function=""/>
    <element name="HIRE_DATE" value="HIREDATE" function=""/>
    <element name="SALARY" value="SAL" function=""/>
    <element name="COMMISSION" value="COMM" function=""/>
   </group>
  </group>
 </dataStructure>
</dataTemplate> 

I have highlighted the main players in a DT:




  • dataTemplate - header element where you provide a name and description for your DT. The name will be used as the root name of the element.
  • properties - these are the runtime switches for the extraction engine:

    • include_parameters - should the parameter values be extracted to the XML or not
    • include_null_Element - if a value when extracted is null do you want an empty element in the xml
    • xml_tag_case - upper or lower case tag names
    • db_fetch_size - maximum number of records to fetch
    • scalable_mode - if you know this is going to be a large dataset or an intensive extract then set this to true
    • include_rowsettag - for simple XML with no hierarchy do you want ROWSET as the root element
    • debug_mode - generate a debug file while processing or not. The test bed I posted will always do this for you.
Moving into the main section:

  • parameters - these are the runtime parameters for the DT
  • lexicals - these are EBS specific and will allow you to fetch flexfield values ... more on these later
  • dataQuery - heres where we start to define the
    sqlStatement - this is where we define the query(s)
  • group - this section defines the structure of the data we want to see and contains
  • element - the reference to the column in a specific query. These also have a name for the resulting XML element and you can include functions here.

So thats a quick overview, lets do the HelloWorld ... its as simple as it gets when it comes to DTs.

<dataTemplate name="HELLOWORLD" defaultPackage="" description="Hello World DT">
   <properties>
      <property name="include_parameters" value="true"/>
      <property name="include_null_Element" value="true"/>
      <property name="xml_tag_case" value="upper"/>
      <property name="db_fetch_size" value="500"/>
      <property name="scalable_mode" value="off"/>
      <property name="include_rowsettag" value="false"/>
      <property name="debug_mode" value="off"/>
   </properties>
   <parameters/>
   <lexicals/>
   <dataQuery>
  <sqlStatement name="Q1" dataSourceRef=""><![CDATA[select 'Hello World!' WELCOME from dual]]></sqlStatement>
 </dataQuery>
   <dataStructure>
  <group name="HELLO" source="Q1">
   <element name="WELCOME" value="WELCOME"/>
  </group>
   </dataStructure>
</dataTemplate>
Running it thru the test bed, this is going to generate the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<HELLOWORLD>
 <HELLO>
  <WELCOME>Hello World!</WELCOME>
 </HELLO>
</HELLOWORLD>
Simple stuff, you can see the relationship now between the DT name - HELLOWORLD, group - HELLO and element name WELCOME.
So thats the basics, next we'll build on the HR schema and introduce some more features.

February 16, 2007

Joining Queries a la Francais

Bonjour mes ami!


I have just got back from a conference in the warmer climes of the French Riviera. Its been quite a shock; after nearly a week of mid 60s temperatures and blue seas coming back to somewhat chilly and mid 30s Colorado has taken some getting used to again. Thankfully the promised thaw is upon us and the piles of snow in my yard are now dwindling, much to my sons' chagrin the snowboard run we had in our back yard is becoming a distant memory. Ahh c'est la vie ... so to another article in the fragmented data template series.

I have spent some time looking at our documentation on data templates, its pretty good so please check it out. Im picking on a series of some of the tougher features to get your head around. for this article its creating and joining multiple queries.


There are two methods to join queries, lets assume we have the following queries:






SELECT DEPTNO,
DNAME,
LOC
FROM DEPT
ORDER BY DEPTNO
SELECT  DEPTNO,
EMPNO,
ENAME,
JOB,
MGR,
HIREDATE,
SAL,
NVL(COMM,0) COMM
FROM EMP


we want to create a data template that will use the DEPT query as the master and the EMP as the child. We need to link the two queries.


Method 1: Query Link

We can create a link similar to Oracle Reports, like this:

<link name="DEPTEMP_LINK" parentQuery="Q1" parentColumn="DEPTNO" childQuery="Q_2" childColumn="DEPTNO"/>


We name the link and then specify the queries and the linking columns, so the <dataQuery> section of our data template will look like:


<dataQuery>
 <sqlStatement name="Q1">
 <![CDATA[
    SELECT DEPTNO,DNAME,LOC from scott.dept
    order by deptno ]]>
 </sqlStatement>
 <sqlStatement name="Q2">
  <![CDATA[
     SELECT  DEPTNO, EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,nvl(COMM,0) COMM
     from scott.EMP ]]>
 </sqlStatement>
<link name="DEPTEMP_LINK" parentQuery="Q1" parentColumn="DEPTNO" childQuery="Q_2" childColumn="DEPTNO"/>
</dataQuery>


It works fine but we have to duplicate the DEPTNO column in the child query. Plus on a more real world query its going to be slower than taking ...

Method 2: Bind Variables


We did alot of testing on the joining of queries and found that usng bind variables was consistently faster then query links, so if you can, and there should not be a reason why you should not, use binds. What do I mena by bind variables, well taking our two queries we are able to have a master-detail relationship between the two i.e. the dept based query is the master and the emp the detail. We can join them quite easily using 'dept_id' as our joingin criteria:

<dataQuery>
 <sqlStatement name="Q1">
 <![CDATA[
    SELECT DEPTNUM,DNAME,LOC
from scott.dept
    order by deptno ]]>
 </sqlStatement>
 <sqlStatement name="Q2">
  <![CDATA[
     SELECT  DEPTNO, EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,nvl(COMM,0) COMM
     from scott.EMP
where DEPTNO = :DEPTNUM ]]>
 </sqlStatement>
</dataQuery>

Notice, I have changed the column names a little just to make the join clearer (dont cut 'n' paste this one.) We use :DEPTNUM from the first query in the where clause of the second, et voila we have two queries joined. Its simpler, less typing and even better its faster!
Salut!

February 19, 2007

Data Templates without grouping

While I have been away I provided two days on in depth techie training to Oracle Consulting in EMEA, they worked me hard (very hard) ranging from inserting barcodes to bursting, all good fun and informative I hope. The second day we spent some time sharing experiences of developing extractions, templates, etc and Im indebted to Serge Vervaet from Oracle Belgium for some great tips. The first of which was on building a data template. The XMLP extraction engine is fast, you can migrate your Oracle Reports to the format relatively easily but there is currently no tool to help you build them so once you have your query sorted out its off to Notepad or an XML editor. For big extracts with lots of columns even I have to admit its very tedious typing out those grouping structures, Serge came up with a way to reduce the tedium and speed up the process by doing away with the grouping section all together - use inline CURSORS in your queries. This creates all the hierarchy you need in your query without the need for the grouping section.


Lets take our old friends the EMP and DEPT queries:







SELECT DEPTNO,
DNAME,
LOC
FROM DEPT
ORDER BY DEPTNO
SELECT  DEPTNO,
EMPNO,
ENAME,
JOB,
MGR,
HIREDATE,
SAL,
NVL(COMM,0) COMM
FROM EMP


To get a hierachical XML output we need to add the following data structure.  


<dataStructure>
  <group name="G_DEPT" source="Q1" groupFilter="">
   <element name="DEPT_NUM" value="DEPTNO" function=""/>
   <element name="DEPT_NAME" value="DNAME" function=""/>
   <element name="LOCATION" value="LOC" function=""/>
   <group name="G_EMP" source="Q2" groupFilter="">
    <element name="EMPNO" value="EMP_NUM" function=""/>
    <element name="EMP_NAME" value="ENAME" function=""/>
    <element name="JOB_TITLE" value="JOB" function=""/>
    <element name="MANAGER" value="MGR" function=""/>
    <element name="HIRE_DATE" value="HIREDATE" function=""/>
    <element name="SALARY" value="SAL" function=""/>
    <element name="COMMISSION" value="COMM" function=""/>
   </group>
  </group>
 </dataStructure>
This is not too bad but imagine a real world data template with 3 or 4 levels ... without that tool its a long and arduous slog! Yes, we need a builder for you, working on it! We can avoid the grouping altogether by combining our queries.


SELECT DEPTNO,
  DNAME,
  LOC,
  CURSOR(SELECT  EMPNO,
           ENAME,
           JOB,
           MGR,
           HIREDATE,
           SAL,
           nvl(COMM,0) COMM 
      from EMP ) as EMP
  from dept
  where deptno = nvl(:p_DeptNo,deptno)


Notice the CURSOR command and the 'as EMP' the latter will create the group G_EMP in the XML data:


<EMPLOYEES>
 <PDEPTNO>10</PDEPTNO>
 <LIST_DEPT>
 <DEPT>
  <DEPTNO>10</DEPTNO>
  <DNAME>ACCOUNTING</DNAME>
  <LOC>NEW YORK</LOC>
  <LIST_G_EMP>
   <G_EMP>
    <EMPNO>7369</EMPNO>
    <ENAME>SMITH</ENAME>
    <JOB>CLERK</JOB>
    <MGR>7902</MGR>
    <HIREDATE>1980-12-17T00:00:00.000-08:00</HIREDATE>
    <SAL>800</SAL>
    <COMM>0</COMM>
   </G_EMP>
    ...
   <LIST_G_EMP>
  </DEPT>
 </LIST_DEPT>
</EMPLOYEES>


So we saved some time and effort and still got structure we needed. Sample DT here. I have to say we have not exhaustively tested the performance but Serge is getting good results and reducing his development time :o)

March 22, 2007

Setting Organizations for Data Templates

Quick one today, trying to work on the bursting series ...
You have developed Data Templates for your EBS instance, but what about setting the org id. In good ol Oracle Reports you just added the following command in your before report trigger:


srw.user_exit('FND SRWINIT')


and it was all done for you. In the world of BIP we go one step further. The executable that runs your data template sets the org id for you so you do not have to do anything.

Now there is a caveat to this, as long as you are using the BIP delivered executable (XDODTEXE) in EBS to call the data engine to run your data templates you're fine ... if you are accessing the data engine via the java APIs or the BIP Enterprise release then its your responsibility to set the org id before the query executes. The simplest way to do it is to call a pl/sql package in the pre-fetch trigger and in there set the org id:


fnd_client_info.setOrgContext(ORG_ID)


then you're set and you'll get the data you are expecting ... well as long as you wrote the query correctly of course ... been there done that, got the T shirt :o)

March 23, 2007

Going out Clobbing?

Another groooovy question today that I thought I had blogged but I was mistaken, that was SQL XML integration.
Let's assume I have XML docs or snippets stored in CLOB columns in a table. How can we get them out into the XML data from the Enterprise server or via SQL for that matter. Here's the XML in the column:


Column                  Contents
WAREHOUSE_ID            1001
WAREHOUSE_NAME          Colorado Big Hoose
WAREHOUSE_SPEC    <?xml version="1.0" ?> 
                  <Warehouse>
                   <Building>Owned</Building>
                   <Area>25000</Area>
                   <Docks>2</Docks> 
                   <DockType>Rear load</DockType>
                   <WaterAccess>Y</WaterAccess>
                   <RailAccess>N</RailAccess>
<Parking>Street</Parking>
<VClearance>10 ft</VClearance>
</Warehouse>


We can use the getClobVal() function in our query thus:


select WAREHOUSE_ID as WAREHOUSE_ID
,      WAREHOUSE_NAME as WAREHOUSE_NAME
,      WAREHOUSE_SPEC.getClobval() as WAREHOUSE_SPEC
from WAREHOUSES


this gets us

<ROWSET>
 <ROW>
 <WAREHOUSE_ID>1001</WAREHOUSE_ID>
  <WAREHOUSE_NAME>Colorado Big Hoose</WAREHOUSE_NAME>
  <WAREHOUSE_SPEC>
  <?xml version="1.0"?>
  <Warehouse>
   <Building>Owned</Building>
   <Area>25000</Area>
   <Docks>2</Docks>
   <DockType>Rear load</DockType>
   <WaterAccess>Y</WaterAccess>
   <RailAccess>N</RailAccess>
   <Parking>Street</Parking>
   <VClearance>10 ft</VClearance>
  </Warehouse>
</WAREHOUSE_SPEC>
 </ROW>
 ...
</ROWSET>


See the embedded '<?xml version="1.0"?>' under WAREHOUSE_SPEC?
Thats not good, BIP is going to choke on that, so we need to remove it. I quickly scoured the Oracle docs on the getClobVal function but did not see anything. Being a lazeee geek I went for the obvious, a substring, does the trick nicely.

select WAREHOUSE_ID as WAREHOUSE_ID
,      WAREHOUSE_NAME as WAREHOUSE_NAME
,      substr(WAREHOUSE_SPEC.getClobval(),22) as WAREHOUSE_SPEC
from WAREHOUSES


we now get:


<ROWSET>
 <ROW>
 <WAREHOUSE_ID>1001</WAREHOUSE_ID>
 <WAREHOUSE_NAME>Colorado Big Hoose</WAREHOUSE_NAME>
 <WAREHOUSE_SPEC>
  <Warehouse>
   <Building>Owned</Building>
   <Area>25000</Area>
   <Docks>2</Docks>
   <DockType>Rear load</DockType>
   <WaterAccess>Y</WaterAccess>
   <RailAccess>N</RailAccess>
   <Parking>Street</Parking>
   <VClearance>10 ft</VClearance>
  </Warehouse></WAREHOUSE_SPEC>
 </ROW>
 ...
</ROWSET>
Valid XML for BIP to consume and we can build a template against it.
Now to go clubbin, techno, techno, techno ... yeah right Dexter, how about a nice cup o tea and a slice o cake ... more your style these daze!

April 10, 2007

Who's on first?

Few questions recently on finding out who is logged into BIP so you can secure a dataset based on the user. In 5.6.2 the support was there, well sort of ... its a nasty work around but can be done. Check out the forum threads for the solution.
Im pleased to say in 10.1.3.2 it's sooo much better.


You can add the following string to your data sets, ':xdo_user_name' to limit the returned data set.
For example:

select  EMPLOYEES.LAST_NAME as LAST_NAME,
  EMPLOYEES.PHONE_NUMBER as PHONE_NUMBER,
  EMPLOYEES.HIRE_DATE as HIRE_DATE,
:xdo_user_name as USERID
from   HR.EMPLOYEES EMPLOYEES
where lower(EMPLOYEES.LAST_NAME) = :xdo_user_name
Notice the use of the lower() function , the xdo_user_name will always be in lowercase format.

BIP does not have a user_id so you need to go with the user name and either use it directly in the query or maybe go against a lookup table to find a user id.
So it's now simple, now you know who's on first


 


 

May 14, 2007

New Look & BIPScriptions

As regular readers will have noticed we have a new look and feel ... playing with CSS and templates has been 'interesting' ... still, may prove useful elsewhere. I have to thank Martin from the ATG Doc Tech team for the banner image ... I have somewhat mangled the original into a montone format that really does not do the original justice, an awesome picture of Oracle HQ. Check out his other pictures on Flickr, some great photos. 


The useful part of this post, Martin's image aside, is the fact that I have added an email feed for the blog. You no longer need come here every day to check for an update, you can get a mail whenever we have something new. Just fill out the 'Get a BIPScription' box on the right hand side bar with your email, confirm the return email and you're all set, BIP updates will be coming to an inbox near you.


 

August 24, 2007

Call for Templates!

You may have read last month about the great BIP Mind Share project, the plan was to provide the ability for development, consultants and customers across all flavors of publisher to share their templates - one big happy family sharing our toys.  


Well, I have had a great meeting recently with the folks that are implementing this wee beasty, one of those folks is Jake of AppsLab and Web2.0 evangelist. Its coming together very quickly and it looks like it is going to be really useful. Once loaded with its meta data to describe said layout or data template you are going to be able to search for that elusive template for that obscure concurrent program that ships with European Localizations. Just as a tease I even have grainy screenshots stolen from the locked cube (yes Oracle's cubes are lockable:0) where developers have been feverishly bashing this out.


ReportsRepository:


Initial release rollout will support EBS and PeopleSoft, but JD Edwards, Siebel, <<put you app name here>> and even standalone publisher templates will be supported in furture releases.


There are a few operational things to iron out such as having a librarian or some means of ensuring the quality of the content, where its going to get hosted, behind Metalink somewhere is currently on the cards. Nothing major to sort out but here's where you come in.


This is only going to be as good as the content that is posted into it. You can expect all of the templates from development teams in EBS and PeopleSoft to be in there. But we need your templates in there too - I know theres a whole question of intellectual property, you have worked hard to get your templates where they are today and why should you share them? But its a give and take process, Im sure that there is going to be a template in there that you need, so if you put one in when taking one out the whole concept is going to be great - if I or any of the development team have helped you with a template I expect to see it in there, got it :0)


You're going to get to see the application at OpenWorld in November and we are looking for volunteer templates to be loaded to our demo system - we need layout templates and a little meta data - what report name and application it's associated and we'll do the rest. We can not guarantee you'll get into the session but we'll try. If you're up for it, mail me your templates, zip em and change the extension to .zipped otherwise our mail server will spit it back at you.

October 18, 2007

Data Dot Defined

After a weekend (there are 3 posts on this) spent in the mountains trudging, sorry 'hiking' up hill and down dale in freezing weather with no sleep I have finally caught the 'cold' that I knew was coming - with a head thats fit to burst I sound ridiculous and 'dalk dike dat, dall der dime' - good for 'd' aliteration fanatics but Im not one of them. There is a useful post coming for those of you using data templates.


If you have moved up to 10.1.3.3 and suddenly you're data template based reports are failing with:


Data not defined


Panic ye not - we have found the issue and its easily fixable. For some reason, we got strict with you in 10.1.3.3, if you had some parameter defined in your data template that you were not passing a value to via the user inteface. If you had a data template like the following, notice the P1 parameter:


<dataTemplate name="prodTemplate" dataSourceRef="demo-hr">
 <parameters>
  <parameter name="P1" dataType="character" defaultValue="Hello"/>
 </parameters>
 <dataQuery>
  <sqlStatement name="prodStmt">
   <![CDATA[
      select last_name, first_name, salary
      from employees
      where salary > 4000
      ]]>
  </sqlStatement>
 </dataQuery>
</dataTemplate>


But you had a report definition that lacked the P1 parameter definition, see the first image.







DT1:     DT2:

It would run in 10.1.3.2 but in 10.1.3.3 we would not let you and we would throw an error.
Until we fix it, to get around the problem either define the parameter in the report, see the second image or remove the parameter from the data template.

October 23, 2007

BLOB, CLOB, RAW and Looooong

The data engine introduced some new support for data types other than the normal varchar, date and number formats in 10.1.3.3. We now have support for the long, raw, clob and blob formats - and you do not have to do anything in the data template to handle these formats.
We auto detect for BLOBs and if it contains an image we will 'base64' encode it and drop that into the XML output so you can then render the image in the output.
If it has text we will return the
escaped format of the text and we only support UTF-8
encoded output. Other BLOB formats we leave alone.
There is an issue for CLOBs around a maximum size retrieved by a data template, that has been addressed in 10.1.3.3.1.

The above applies to 'data template' based reports. If you are using straight SQL things are a little more limited. We have BLOB and CLOB support and the auto detect for images is working but we do not have support for RAW and LONG column types at the moment - we're looking into that for a future release.

January 29, 2008

Data Template Building & Performance

I have been working with data templates for the last few daze ... we are doing some work on getting some performance numbers - at last. I have been building some more complex data templates than those I have built previously on the blog. The engine is extremely powerful and can do alot of the heavy lifting for you when it comes to grouping hierarchy and summary amounts.


Here's my latest.


<dataTemplate name="EMPLOYEES" defaultPackage="" dataSourceRef="demo-hr">
 <properties>
  <property name="include_parameters" value="true"/>
  <property name="include_null_Element" value="true"/>
  <property name="include_rowsettag" value="false"/>
  <property name="scalable_mode" value="off"/>
  <property name="db_fetch_size" value="300"/>
 </properties>
 <parameters/>
 <lexicals/>
 <dataQuery>
  <sqlStatement name="LOCATIONS" dataSourceRef="">
   <![CDATA[select  COUNTRIES.COUNTRY_NAME as COUNTRY,
  REGIONS.REGION_NAME as REGION,
  LOCATIONS.STREET_ADDRESS as STREET_ADDRESS,
  LOCATIONS.POSTAL_CODE as POSTAL_CODE,
  LOCATIONS.CITY as CITY,
  LOCATIONS.STATE_PROVINCE as STATE_PROVINCE,
         LOCATIONS.LOCATION_ID as LOCATION_ID1
 from  HR.LOCATIONS LOCATIONS,
  HR.COUNTRIES COUNTRIES,
  HR.REGIONS REGIONS
 where   COUNTRIES.REGION_ID=REGIONS.REGION_ID
 and  COUNTRIES.COUNTRY_ID=LOCATIONS.COUNTRY_ID]]>
  </sqlStatement>
  <sqlStatement name="DEPTS" dataSourceRef="">
   <![CDATA[select  DEPARTMENTS.DEPARTMENT_ID as DEPARTMENT_ID1,
  DEPARTMENTS.DEPARTMENT_NAME as DEPARTMENT_NAME
from  HR.DEPARTMENTS DEPARTMENTS
where   DEPARTMENTS.LOCATION_ID = :LOCATION_ID1]]>
  </sqlStatement>
  <sqlStatement name="EMPLOYEES" dataSourceRef="">
   <![CDATA[select     EMPLOYEES.EMPLOYEE_ID as EMPLOYEE_ID,
     EMPLOYEES.FIRST_NAME||' '||EMPLOYEES.LAST_NAME as EMP_NAME,
     EMPLOYEES.EMAIL as EMAIL,
     EMPLOYEES.PHONE_NUMBER as PHONE_NUMBER,
     EMPLOYEES.HIRE_DATE as HIRE_DATE,
     EMPLOYEES.SALARY as SALARY,
     EMPLOYEES.DEPARTMENT_ID as DEPT_ID,
     JOBS.JOB_TITLE as JOB_TITLE
 from     HR.JOBS JOBS,
     HR.EMPLOYEES EMPLOYEES
 where   EMPLOYEES.JOB_ID=JOBS.JOB_ID
 and department_id = :DEPARTMENT_ID1 ]]>
  </sqlStatement>
 </dataQuery>
 <dataStructure>
  <group name="G_REGION" source="LOCATIONS" groupFilter="">
   <element name="LOC_REGION" value="REGION" function=""/>
   <group name="G_LOC" source="LOCATIONS" groupFilter="">
    <element name="LOC_ADDR" value="STREET_ADDRESS" function=""/>
    <element name="LOC_CITY" value="CITY" function=""/>
    <element name="LOC_CODE" value="POSTAL_CODE" function=""/>
    <element name="LOC_STATE" value="STATE" function=""/>
    <element name="LOC_COUNTRY" value="COUNTRY" function=""/>
    <group name="G_DEPT" source="DEPTS" groupFilter="">
     <element name="DEPT_NUM" value="DEPARTMENT_ID1" function=""/>
     <element name="DEPT_NAME" value="DEPARTMENT_NAME" function=""/>
     <element name="DEPT_SALARY" value="G_EMP.SALARY" function="sum()"/>
     <element name="DEPT_COUNT" value="G_EMP.EMPLOYEE_ID" function="count()"/>
     <group name="G_JOB" source="EMPLOYEES" groupFilter="">
      <element name="JOB_TITLE" value="JOB_TITLE" function=""/>
      <group name="G_EMP" source="EMPLOYEES" groupFilter="">
       <element name="EMPNO" value="EMPLOYEE_ID" function=""/>
       <element name="EMP_NAME" value="EMP_NAME" function=""/>
       <element name="HIRE_DATE" value="HIRE_DATE" function=""/>
       <element name="PHONE_NUMBER" value="PHONE_NUMBER" function=""/>
       <element name="EMAIL" value="EMAIL" function=""/>
       <element name="SALARY" value="SALARY" function=""/>
      </group>
     </group>
    </group>
   </group>
  </group>
 </dataStructure>
</dataTemplate> 


Maybe not such a real data case but it shows off some of what you can do with the data engine. You can see I have 3 queries using bind variables LOCATIONS -> DEPARTMENTS -> EMPLOYEES. Now, rather than just then render the three levels in the grouping hierarchy I have created 5 levels:


G_REGION
    |
    |_ G_LOCATION
            |
            |_ G_DEPT
                     |      
                     |_ G_JOB
                              |
                              |_G_EMP

I have introduced the G_REGION and G_JOB levels into the data. Even thou we only have 3 levels in the queries the engine will calculate the hierarchies to be applied to the data.

The one drawback with the data templates is the fact that we do not have a builder for creating the templates. Good news on the way, I have been testing a new data template builder. This allows you to build your queries,


DTBuilder1:


set appropriate engine flags


DTBuilder2:


then bind them together into the grouping hierarchy


DTBuilder3:


and finally set properties for the individual columns.


DTBuilder4:


Its a nice tool - needs a little tweaking but its very close - of course usual disclaimers apply and it may look completely different by the time it hits the shelves. Look for it in a release coming soon ... performance numbers will come sooner.

February 11, 2008

JD Edwards Connections

Its been a week of new stuff to test - late last week I got to check out the new JD Edwards E1 JDBC driver that they are working on. This is going to enable folks to point BIP at the JDE instance and start creating reports against it.


JDE1:

Rather than the 'special' data types it would typically return directly from the database you are going to get BIP friendly data that you can work with. This is going to open up JDE E1 instances to BIP and make life so much easier for folks.


JDE2:


Another conversation we had last week was with Sharon Winter  and her team, who looks after the ever popular 'green screen' JDE World application. We are working toward opening up 'World' to the world of BIP along similar lines to E1.


No release dates as yet but suffice to say its coming. Here's an early welcome to all you JDE folks that are waiting to use standalone BIP those of you in E1 have an embedded version to use already. If you're looking for docs on how to use the embedded version check the user guide out here.

May 9, 2008

Get Swimming!

Another tenous and cryptic title, today we're talking connection pooling with BIP.


We have had a question recently on how to set up a connection pool for BIP rather than direct JDBC connections. Pooling has its advantages of course - rather than each report having its own 'personal' connection to the database at runtime it which can get expensive very quickly in terms of resources. Instead, BIP can pick up a connection from a pool, assign it to the report, once the data is fetched the connection is released back to the pool.


Now BIP provides a page to enter the JNDI connection pool information but we are little vague or we at least expect you to know what you are doing when it comes to setting up the pool in the J2EE container. To supplement the doc I thought I'd walk through setting up the connection pool in OC4J and then how to get BIP to use it.


Firstly, you are going to need the oc4jAdmin password to be able to do any of this. If you installed BIP on your server then the installer will have prompted you for a password - hopefully you can remember it. If someone else installed it for you - either talk nicely to them to get the password or buy them a beer and get them to read this blog article and get them to set up the pool. Once you have the password login to the Enterprise Manager, typically:


http://server:port/em


This will get you to the main home page.

Pooling1:

Now click on the Administration tab and then on the Services > JDBC Resources icon. You'll end up here:


Pooling2:


There is a sample pool and data source set up already for reference. We are going to create a new pool and datasource to use it. Click on the 'Create' button for the Connection Pools section.


Pooling3:


Select the BIP application and 'New Connection', this is going to make the pool available to the BIP server. Now hit continue.
Fill out the form providing a name for the pool, etc. Check the documentation if you need to get into Connection Factory Properties for your database.


Pooling4:


Before you move on test the connection.


Pooling4:


Now move to the Attributes page to fill out the size of the pool, etc. Worth reading the OC4J doc here to get some advice on the numbers to use. It going to depend on your expected reporting load on the system, etc.


Pooling5:


The Proxy Interfaces tab is only needed to wrap vendor implementations of java.sql objects when using managed data sources. I dont need it as Im using Oracle objects - check the doc if you need more info.
The pool is now set up so click Finish. Next we need to create a DataSource for the pool - this will manage the pool.


Pooling6:


Assign the Application and select Managed Datasource to allow OC4J to 'look after' it for us. 


Pooling7:


Fill out a name for the dataource and enter the JNDI location. Check the doc for more details but here's what it states on the JNDI name.


Enter the Java Naming and Directory Interface (JNDI) location (or path) for the data source. Deployed applications use this information to locate the data source. For example, the JNDI location of the OracleManagedDS data source is jdbc/OracleManagedDS.


The one I created has the following location, 'jdbc/BIP10gSource', its a virtual location so you will not find it on the disk. The location is important as we are going to reference it in the BIP JNDI connection setup. Once you're done click Finish, you are taken back to the main data source page where you can test the datasource. We now have a pooled connection we can use in the BIP server to a database.


Now bounce the OC4J instance and bring up the BIP server and get in as an Administrator and navigate to the JNDI Connection page (Admin > Datasources > JNDI Connections) and Add Data Source.


Give it a name and the JNDI location that you defined in the OC4J UI - then hit Test Connection to make sure its all working.


Pooling8:


You can now use the connection for your reports - just select the pool name rather than a direct database connection. You're now swimming so gimme 30 laps!

May 13, 2008

External Data Engines I

Well it's almost mid May here in Colorado, so you know what that means, yep, its snowing! Not too heavily but the temperature has dropped from a balmy 70F yesterday to a chilly mid 30s today. Apparently we need it, inspite of nearly a 120% of average snowpack for the year we are facing drought conditions in Colorado this summer - so we are xeriscaping our garden. My wife and I are enjoying a discussion around me wanting to rip up the water hungry grassy turf we have in our yard and replacing it with concrete ... Im kidding. Anyhoo, enough 'weather' related musings ... remember Im English, we are nearly all obssessed with it.


Most of you that use the BIP Enterprise release know that it can get data from almost anywhere, files, dbs, web services even Excel with a little help. I have had a few questions about the 'how' on getting 'remote', non-database data i.e. a 'data engine' already exists and it serving up XML. I thought I would cover some of those and end on the big question that I have been working on with a customer. What about a data engine that BIP can not connect directly to? This tied in with, 'we dont want to use your scheduling engine we have our own, how can it work with BIP?' Tasty questions, we'll come back to later.


So, what are the options for fetching data  from an existing data engine? Thats going to depend on how connected your engine is to your network. As I mentioned above BIP can connect to data sources in multiple ways.


HTTP/S - if your engine can serve up data upon an HTTP request, then BIP can call it and pass parameters on the URL. If you can write a servlet that can call the engine and serve up the data on its behalf you could go down that route too.


Web Services - can you wrap your engine in a web service? Again, BIP can call for the data via the service and pass parameters into the service to influence the data set.


XML File - this is probably the simplest method. IF your engine can generate a file and drop it into an accessible directly for BIP to pick up then you have a solution. Its a simple case of registering the directory with BIP and then setting up the filename in the report definition for BIP to look for.


Excel - this needs a little effort to get the data from the file but we provide the APIs you just need the servlet to read it and serve up the XML data to BIP upon request. 


All of these options will require the BIP report to be scheduled to pull the data periodically. In the first two cases thats probably OK, BIP is making an indirect call to the data engine requesting data at the time the report runs. In the last two cases BIP is going to rely on the files being refreshed prior to the scheduled report running otherwise you are going to see the same data.


How can we get away from this 'pull' model where a report is scheduled to pick up the data, what if we wanted to control the timing of the report, say those XML files were being dropped into a directory periodically and each time the new file was dropped in we wanted to get BIP to run the report. While we are at it, maybe we have retail branch sales data coming into a central server, all of those XML files have the same data structure. We do not want to define a report for each branch,  we only want a single report definition that picks up the appropriate filename at runtime, processes it and sends an email to the branch manager with their results and a copy to corporate. I could go on, but thats enough of a scenario for the time being. Next time I'll dig in and talk about how you might tackle just such a scenario using BIP.

May 15, 2008

External Data Engines II

A mad few daze getting presentations and demos ready for internal meetings on BIP for Fusion Apps. Yep, it's coming together and its going to be good from a user and a developer point of view. I want to say its going to be 'sweeeeet', my son's favorite answer for all things good at the moment, but I won't.


Back to data engines, I said I would get into the more complex case that I stated at the end of the last entry.


How can we get away from this 'pull' model where a report is scheduled to pick up the data, what if we wanted to control the timing of the report, say those XML files were being dropped into a directory periodically and each time the new file was dropped in we wanted to get BIP to run the report. While we are at it, maybe we have retail branch sales data coming into a central server, all of those XML files have the same data structure. We do not want to define a report for each branch, we only want a single report definition that picks up the appropriate filename at runtime, processes it and sends an email to the branch manager with their results and a copy to corporate.

This is a real use case scenario I worked on with a customer recently andI believe they are well on their way to implementing it. Branches would periodically drop their data files into a central directory and need a report sent back tout suite. The first hurdle to over come is how to invoke Publisher when a new file hits the directory? This customer has a centralized scheduler called Control-M - fairly widely used I think. This product has the ability to act as a directory daemon looking for files as they are dropped into a directory and then invoking some other process - theres our hook to get BIP to execute a report. Its not that tough to create your own in Perl or similar language. Here's the architecture I came up with:


ExtData1:


Whats going on?


The Control-M product is constantly polling a specific directory looking for new or updated files. These are XML data files from the branches, when a new file appears it invokes the shell script, this is passed the filename as a parameter. It in turn invokes the java web service client class that then calls the BIP server to run a given report. The web services we provide allow you to have tight control over the report, run it now?, run it later?, which template to use?, what output to generate?, where to deliver it? All of this information can be passed to the WS client code from the shell script or you could have the WS client class to parse the XML to find out this type of information e.g. the branch's email address.


On the BIP server we have a single report defined that uses an HTTP data source with the filename as a parameter. This calls a servlet that is looking over the directory and based on the incoming filename parameter. I guess you could just have a servlet acting as the daemon to recognise new/updated files and make a call back to BIP. In this case Control-M is orchestrating the whole process so the servlet is just returning the XML file to BIP for processing.


Thats it really, the only point of note is the return codes that get passed back to the WS class and thence to the script and ultimately back to the Control-M application. Fairly simple architecture, that allows another application to control the BIP server. If you have such a requirement, I'd love to hear from you!

June 10, 2009

Data Template Progression

I recently spent some time putting together a progression of data templates (DT) for a customer demo. Its a series of data templates that move you from the simple to the more complex, two query, before fetch trigger, group filter, summarizing all singing all dancing data template. I have heard requests this week for a three'er - looking at that. For now, satisfy yourselves with what I have.

Yep, still no builder but the 11g data builder is so cool it makes my geeky mouth water. Can not post images for fear of product management reprisal and bats up night dresses in the small hours (go Google it :0)
In spite of the lack of a DT builder I found it quite useful and so much faster to start off with the queries, get them working and returning data based on the parameters and to then move to the grouping, group filters (please dont use em) and summary columns. Rather than trying to write the whole this in one hit and then spend forever debugging.

During the demo we discussed the error messages and means to debug that BIP offers. Some of the layout errors are still mystifying at times '1=1' and '9<10' come to mind as true but very annoying error messages. You'll be pleased to here that turning on the debug for the data templates provides a mass of information about what is going on and of course what did not happen and why.

If you're interested you can get the progression here. Just unpack them under a directory under the BIP reports home directory. Fire up BIP and they will be available. They all run against the demo HR schema in an Oracle db. There is also a sql script for the supporting plsql package for the triggers.
Finally, just because its amusing and I wanted to share the mirth, check out this funny developer 'joke' from Bex on his blog, made me chortle!

September 18, 2009

Formatting Concatenated Datasources

When you are building reports inside BIP standalone, do you concatenate or data template? If you need to use multiple queries in your report, either in the same database or separate and you need to join that data together in the output, what do you use? Or maybe some other system is spitting out a flattened hierarchy XML.

You have a choice, either use a data template to bring the data together simplifying the template or use the concatenated data source and use some funky tricks in the layout to bring the separate data together.

Let's assume I have vendors in an Oracle db and their purchase orders in a SQL server database. I need to generate a listing report showing vendors and their POs. I have covered data templates elsewhere in this blog and I will follow up with a multi database example next week. Today, lets deal with the concatenated data source. Say I have the following data:

<DATA>
<SUPPLIERLIST>
 <G_VENDOR_NAME>
  <VENDOR_NAME>Nuts and Bolts Limited</VENDOR_NAME>
  <ADDRESS>1 El Camino Real, Redwood City, CA 94065</ADDRESS>
  <VENDOR_ID>1</VENDOR_ID>		
 </G_VENDOR_NAME>
 <G_VENDOR_NAME>
  <VENDOR_NAME>Big Bike Motorsports</VENDOR_NAME>
  <ADDRESS>10 Downing Street, London, SW1, UK</ADDRESS>
  <VENDOR_ID>2</VENDOR_ID>		
 </G_VENDOR_NAME>
</SUPPLIERLIST>
<PO_LIST>
 <G_PO>
  <PO_VENDOR>1</PO_VENDOR>
  <PO_NUM>123</PO_NUM>
  <PO_ITEM>TV</PO_ITEM>
  <PO_AMT>100</PO_AMT>
 </G_PO>
 <G_PO>
  <PO_VENDOR>1</PO_VENDOR>
  <PO_NUM>124</PO_NUM>
  <PO_ITEM>Monitor</PO_ITEM>
  <PO_AMT>300</PO_AMT>
  </G_PO>
 <G_PO>
  <PO_VENDOR>1</PO_VENDOR>
  <PO_NUM>125</PO_NUM>
  <PO_ITEM>PC</PO_ITEM>
  <PO_AMT>400</PO_AMT>
 </G_PO>
 <G_PO>
  <PO_VENDOR>2</PO_VENDOR>
  <PO_NUM>233</PO_NUM>
  <PO_ITEM>TV</PO_ITEM>
  <PO_AMT>1000</PO_AMT>
 </G_PO>
 <G_PO>
  <PO_VENDOR>2</PO_VENDOR>
  <PO_NUM>234</PO_NUM>
  <PO_ITEM>Receiver</PO_ITEM>
  <PO_AMT>340</PO_AMT>
 </G_PO>
 <G_PO>
  <PO_VENDOR>2</PO_VENDOR>
  <PO_NUM>235</PO_NUM>
  <PO_ITEM>Phone</PO_ITEM>
  <PO_AMT>150</PO_AMT>
 </G_PO>
</PO_LIST>
</DATA>

You'll notice both the vendors and the PO are at the same level in the XML hierarchy. Looks tough to bring them together in the output to get

VENDOR1
PO1
PO2
VENDOR2
PO1
PO2.

With a very simple template approach we get

ConcatDS1.jpg

Not what you need right?

Looking back at the data, you might also have noticed that both the vendor and PO share the vendor ID - we can use that. In our RTF template we are going to loop over the vendors and inside loop over the PO but checking vendor ID to ensure we are getting the right POs.

There are two pieces to the solution that are different to a regular template loop over or re-grouping over a regular hierarchical or completely flat data set.

ConcatDS3.jpg


Variable - as we loop over the vendors we need some way of tracking what vendor record we are currently processing. In my template just after the for-each for the G_VENDOR group I have field with

<?variable@incontext:v_id;VENDOR_ID?<

Its not really a variable, you can not update with a value, you can just reference it or re-declare it. Im using the @incontext command to keep the variable localized to the vendor loop.

The other piece of interest is the loop command for the POs.

<&?for-each:/DATA/PO_LIST/G_PO[PO_VENDOR= $v_id]?>

Two things of note in here:

  1. The for-each is not using 'G_PO' as you might expect. Im providing the complete path to the PO group. Why? Because Im currently looping over the vendors in an outer loop. Remember POs are at the same level as the vendors so <?for-each:G_PO?> will not find any data.

  2. There is an XPATH expression associated with the for-each, '[PO_VENDOR= $v_id]' - this is instructing the rendering engine to only show PO records that have a vendor id equal to the parameter, 'v_id' - remember that was set and re-set every time you hit a new vendor in that loop.

That's it, those two pieces of 'code' allow you to generate a hierarchical output from the concatenated data set.

ConcatDS2.jpg


If you need the template and some data, get em here.

Yep, its a simple demo but you get the idea. Of the two options, layout or data template - Id go with the data template every time. Get the extraction engine to do the heavy lifting and KISS on the layout template :0) Complex layout templates mean scared business users that will not touch the template even if it is in friendly ol' MSWord. Im excluding version 2007 from that term of endearment, I have had it 6 months and I still don't like it!

Enjoy yer weekend, after my week, I'll be sleeping!

September 21, 2009

Multi Database Data Templates

In my post on Formatting Concatenated Datasources I mentioned the better way to do thangs was to use a data template to connect and execute queries on each databases and bring back a grouped/formatted data set.
So rather than the concatenated data set, thus

<DATA>
<SUPPLIERLIST>
 <G_VENDOR_NAME>
 <G_VENDOR_NAME>
</SUPPLIERLIST>
<PO_LIST>
 <G_PO>
 <G_PO>
 <G_PO>
 <G_PO>
 <G_PO>
 <G_PO>
</PO_LIST>
</DATA>

we use a data template to generate a hierarchical data set thus:

<DATA>
 <G_VENDOR>
 <G_PO>
 <G_PO>
 <G_PO>
 <G_VENDOR>
 <G_PO>
 <G_PO>
 <G_PO>
</DATA>

Remember, this is executing a query on SQLServer (or other db) to fetch the VENDOR data and executing another 'child' query on Oracle (or another db) to fetch the vendor's PO data.
This will allow you to simplify the layout template removing the need for the variables and the XPATH expression. It will also improve the performance immensely. With the concatenated data sources we were looping over the vendors. Setting a variable for the vendor and then looping over all of the POs filtering out the ones we did not want. So for every vendor we had to loop over every PO - its expensive.
With the data template approach we just following the data structures


<?for-each:G_VENDOR?>
...
<?for-each:G_PO?>
...
<?end for-each?>
<?end for-each?>

Much less expensive and simpler too and that satisfies the other mantra for layout templates, KISS!

How do you do it then?
Well first off you need to set up the oracle and SQL Server datasources. In 10.1.3.4 we ship the drivers for you for the popular non-Oracle dbs so setting them up is a relative cinch. We even provide a simple connection string for you to fill out.

MultiDT1.jpg

NB: if you are using SQL Server 2008, you can use the 2005 driver. Worked for me.

Once you have both data sources set up its then on to the data template. As I have mentioned previously, there is no 'builder' for data templates in 10.x releases. The world becomes a much nicer place with 11g but you'll have to wait a bit longer for that. If you are just starting out with data templates, check out the Data Template Progression entry to get started. Once you are comfortable with the format come back ...

You ready? Its not that tough to do, you just need to get the structure right. I fell in to a trap putting this example together (thanks Ahmed) but I can now share that with you. Here's the DT for the SQLServer (parent) and Oracle (child) queries and their structure.

<dataTemplate name="DATA" description="SQL Server - Oracle Extraction">
	<properties>
		<property name="include_parameters" value="true"/>
		<property name="include_null_Element" value="false"/>
		<property name="include_rowsettag" value="false"/>
		<property name="scalable_mode" value="off"/>
		<property name="debug_mode" value="on"/>
	</properties>
	<dataQuery>
		<sqlStatement name="VENDORS" dataSourceRef="locsqls"><![CDATA[
         select     VENDORS.VENDOR_ID as VENDOR_ID,
     VENDORS.VENDOR_ADDRESS as VENDOR_ADDRESS,
     VENDORS.VENDOR_NAME as VENDOR_NAME
         from     biptest.dbo.VENDORS VENDORS]]></sqlStatement>
		<sqlStatement name="POS" dataSourceRef="lorcl"><![CDATA[
           select po.PO_VENDOR as PO_VENDOR,
       po.PO_NUM as PO_NUM,
       po.PO_ITEM as PO_ITEM,
       po.PO_AMT as PO_AMT
           from     PO_TABLE po
       where po.po_vendor = :VENDOR_ID]]></sqlStatement>
	</dataQuery>
	<dataStructure>
		<group name="G_VENDOR" source="VENDORS">
			<element name="VENDOR_ID" value="VENDOR_ID"/>
			<element name="VENDOR_NAME" value="VENDOR_NAME"/>
			<element name="VENDOR_ADDRESS" value="VENDOR_ADDRESS"/>
			<group name="G_PO" source="POS">
				<element name="PO_NUM" value="PO_NUM"/>
				<element name="PO_ITEM" value="PO_ITEM"/>
				<element name="PO_AMT" value="PO_AMT"/>
			</group>
		</group>
	</dataStructure>
</dataTemplate>


I have emboldened the important parts where things deviate from a regular single db data template. Notice the dataSourceRef attributes on the sqlStatement element. These names need to match the ones you defined earlier.
Then the query link, notice Im using a bind variable (:VENDOR_ID). You can not use a link structure i.e.

<link name="SQLORCL_LINK" parentQuery="VENDORS" parentColumn="VENDOR_ID" childQuery="POS" childColumn="PO_VENDOR" condition="="/>

they are not supported. Something else of note, I did not hit it here but if you had VENDOR_ID on the SQLServer table and VENDOR_ID on the Oracle table you must ensure they have a different alias e.g. VENDOR_ID and VENDOR_ID1. Although they are on different dbs when it comes to linking them

where PO.VENDOR_ID = :VENDOR_ID

will at best confuse the heck out of the extraction engine and get unexpected results.

One other trap that I somehow fell into was to have each query under its own </dataQuery> element. they need to share one.

<dataQuery>
 <sqlStatement name="VENDORS" dataSourceRef="locsqls"><![CDATA[ SQL1 ]]>
 </sqlStatement>
 <sqlStatement name="POS" dataSourceRef="lorcl"><![CDATA[]]>
 </sqlStatement>
</dataQuery>

Otherwise you get weird table does not exist errors.

Once you have the data template in your report and valid you will get the data set back you want. You can then start adding the parameters, summary columns, etc you want.

<COMBINED>
<LIST_G_VENDOR>
<G_VENDOR>
<VENDOR_ID>1</VENDOR_ID>
<VENDOR_NAME>Nuts and Bolts Limited</VENDOR_NAME>
<VENDOR_ADDRESS>1 El Camino Real, Redwood City, CA 94065</VENDOR_ADDRESS>
<LIST_G_PO>
<G_PO>
<PO_NUM>123</PO_NUM>
<PO_ITEM>TV</PO_ITEM>
<PO_AMT>100</PO_AMT>
</G_PO>
<G_PO>
<PO_NUM>124</PO_NUM>
<PO_ITEM>Monitor</PO_ITEM>
<PO_AMT>300</PO_AMT>
</G_PO>
<G_PO>
<PO_NUM>125</PO_NUM>
<PO_ITEM>PC</PO_ITEM>
<PO_AMT>400</PO_AMT>
</G_PO>
</LIST_G_PO>
</G_VENDOR>
<G_VENDOR>
<VENDOR_ID>2</VENDOR_ID>
<VENDOR_NAME>Big Bike Motorsports</VENDOR_NAME>
<VENDOR_ADDRESS>10 Downing Street, London, SW1, UK</VENDOR_ADDRESS>
<LIST_G_PO>
<G_PO>
<PO_NUM>233</PO_NUM>
<PO_ITEM>TV</PO_ITEM>
<PO_AMT>1000</PO_AMT>
</G_PO>
<G_PO>
<PO_NUM>234</PO_NUM>
<PO_ITEM>Receiver</PO_ITEM>
<PO_AMT>340</PO_AMT>
</G_PO>
<G_PO>
<PO_NUM>235</PO_NUM>
<PO_ITEM>Phone</PO_ITEM>
<PO_AMT>150</PO_AMT>
</G_PO>
</LIST_G_PO>
</G_VENDOR>
</LIST_G_VENDOR>
</COMBINED>

Nice!

In terms of performance, its a much better way to get formatted/grouped data in your layout. But as I was discussing with someone this morning. Its a balancing act, you really want the dbs and the extraction engine to do the heavy lifting in terms of nesting structure, summary columns and sorting but you also want to provide the folks who build templates for users as much flexibility as possible.
Its not an exact science by any means, I always try and at least have the data engine extract using a 'sensible' hierarchy. Most if not all users are going to want to list invoices showing header information followed by the lines. Very rarely will they want to turn that hierarchy on its head, so its a good structure to have the DT extract plus the summary columns and sorting that go with it e.g invoice total, sort by line number, etc.

If you want to see the report file, you can get it here.

Happy Data Templating!

About Data Extraction

This page contains an archive of all entries posted to Oracle BI Publisher Blog in the Data Extraction category. They are listed from oldest to newest.

Bursting is the previous category.

Document Delivery is the next category.

Many more can be found on the main index page or by looking through the archives.

Top Tags

Powered by
Movable Type and Oracle