Main

Document Delivery Archives

August 3, 2006

Document Delivery from EBS - Part 1

So many questions on the forum about the delivery manager and its integration into the concurrent manager in EBS, so over the next few postings Im going to try and clear the air and show you some of the options you have to get documents delivered from the concurrent manager, forms or OAFramework. 

Introductions
We need an introduction to the delivery manager before we start. Its a rich set of java APIs that can be used to send documents via:


  • Email - pretty obvious, supports multiple document attachments, embedded HTML, multiple recipients, etc
  • Fax - not up to communicating with a multifax board for high volume but definitely adequate
  • Print - again obvious, uses the IPP for communication to the printer, has tray handling, copies, duplex, orientation, etc
  • WebDAV - allows you to post documents to a repository such as Oracle Files Online, other 3rd party management solutions
  • HTTP/HTTPS - posting docs to a web server
  • FTP/SFTP - posting docs to an FTP
  • Custom - if you have your own channel or a third party delivery solution you (or they) can create a custom delivery channel interface and XMLP will direct documents down that channel.

It even has the AS2 protocol implemented for your EDI messages. The APIs can rely on a configuration file to define servers, printers, etc or you can hardcode the information directly in your API call. 

Clarifications
A very rich set of APIs that are being expanded with every release ... thats the good news. The bad news is that none of it is directly integrated into the concurrent manager, OA framework or forms ... if you want to use the functionality you are going to have to get into some java coding and customization.

OA Framework
As OAF is completely java anyway its not going to be a tough job to create some UI for folks to deliver docs.

Forms
As EBS11i uses Forms 6i, our options are a little limited to integrate the delivery APIs directly. We could create some plsql wrappers for the APIs ... thats been done by a couple of Apps teams already. Or we could just call a concurrent program from the form. 

Concurrent Manager
This is the most popular method of generating reports, lets just cover what is going on process wise in the concurrent manager today (11i.10.X) with an XMLP report.



  1. User selects an XMLP report to execute and enters the parameter values they want, the layout template, output format and language then submits
  2. Concurrent manager 'looks up' the concurrent program definition, finds the executable and calls it. This could be an XMLP Data Template, Oracle Report, pl/sql or any other extraction routine ... just so long as its getting XML data.
  3. Once the program has completed the Output Post Processor(OPP) comes into play. This is the new concurrent manager that handles the XMLP requests. It is passed a handle to the data file and calls the XMLP APIs to process the data to then generate the output based on the users choice of template, format, etc. The output document is then generated and the XML data is preserved.
  4. The user can then either view the document from the SRS forms and print locally or they might have asked that the document be printed directly to a printer. Hopefully the SysAdmin has set up PASTA to handle the document, pre-process it to a printer language and then pass it off to the printer.


Thats the current process in a nutshell. If we want to add delivery other than printing there are a couple of entry points for us to inject our own delivery options at:
Step 2: We can create a wrapper java concurrent program. This would be called by the concurrent manager instead of the current one. It would:


  • Call the extraction conurrent program and wait for it to finish
  • Call the XMLP formatting APIs to generate the output document i.e no OPP involvement
  • Call the delivery APIs to deliver the document.
This approach has some advantages, you have complete control over everything that is going on, you can add parameters to the program so the user can select the delivery destination for the document at runtime, etc ... but there is a big disadvantage ... you are going to need a wrapper for every program or create a request set for every report containing the base report and the delivery program as a second program that can accept the request id of the first ... thats a hurdle in itself ... the programs in the request set are not 'aware' of each other and at runtime the CM does not pass info about them to each other e.g. request IDs ... been there done that ... its a nasty piece of code that has to make too many assumptions to work.

Step 4: We can create a virtual print destination in EBS that does not have a printer at the end of it but rather a shell or perl script that can take the document and deliver it down a channel based on the user's choice at runtime.
This has the advantage that we do not need to create custom programs, mess with the definitions, etc. It's another moving part but its at least everything we create is sitting at the end of the reporting flow.  

The second option seems to be the most popular entry point for many third party delivery solutions. Im going to pursue this line of investigation over the next few postings.
Next ... getting into the delivery APIs!

August 4, 2006

Document Delivery from EBS - Part II

Carrying on from where we left off yesterday in ths series of articles on the XMLP delivery manager, today we'll take a look at the APIs themselves and how you can use ... we're not going to touch EBS yet, just get some basics around the APIs. Before we start, arm yourself with the following documents:

XML Publisher Users Guide incl Developers Guide
XML Publisher release 5.6.1 Core Components API (javadoc)

Overview
The basic flow to deliver documents is as follows:



  1. Create DeliveryManager instance
  2. Create DeliveryRequest instance by createRequest() method
  3. Add request properties such as where to send to the DeliveryRequest. Most of properties require a string value. Take a look at the supported properties of each delivery channel for more detail.
  4. Set your document to the DeliveryRequest
  5. Call submit() to submit the delivery request.

One delivery request can handle one document and one destination. This is because it makes simple and easy to track down each delivery status and re-submit the request if it failed to deliver.


The DeliveryRequest allows you to set the documents in two ways, these are;


  • Getting OutputStream from the DeliveryReqeust and writing the document to the OutputStream. You don't need to close the OutputStream, but you can just call submit() method right after you finish writing the document to the OutputStream.-->
  • Setting InputStream of the document to the DeliverRequest. The DeliveryRequest will read the InputStream when you call submit() for the first time. The DeliveryRequest doesn't close the InputStream so you need to take care of closing it.
  • Setting the file name of the document to the DeliveryRequest. The DeliveryRequest will pick up the document from the file system when you call submit().

Those are the basic steps you need to follow, how about in practice?


EMail
With all of the delivery channels its a case of setting the required properties and then calling the apprpriate API. 


create delivery manager instance
   DeliveryManager dm = new DeliveryManager();
   // create a delivery request
   DeliveryRequest req = dm.createRequest(DeliveryManager.TYPE_SMTP_EMAIL);
   // set email subject
   req.addProperty(DeliveryPropertyDefinitions.SMTP_SUBJECT, "Invoice");
   // set SMTP server host
   req.addProperty(
     DeliveryPropertyDefinitions.SMTP_HOST, "mysmtphost");
   // set the sender email address
   req.addProperty(DeliveryPropertyDefinitions.SMTP_FROM, "myname@mydomain.com");
   // set the destination email address
   req.addProperty(
     DeliveryPropertyDefinitions.SMTP_TO_RECIPIENTS, "user1@mydomain.com, user2@mydomain.com" );
   // set the content type of the email body
   req.addProperty(DeliveryPropertyDefinitions.SMTP_CONTENT_TYPE, "application/pdf");
   // set the document file name appeared in the email
   req.addProperty(DeliveryPropertyDefinitions.SMTP_CONTENT_FILENAME, "invoice.pdf");
   // set the document to deliver
   req.setDocument("/document/invoice.pdf");
    // submit the request
   req.submit();
   // close the request
   req.close();



Simple stuff right?

This was just a simple example, you have complete control over the email and its attachments, there are more examples in the developer guide and the javadocs for the APIs.


FAX
The Delivery API supports the delivery of documents to fax modems configured on CUPS using the Internet Printing Protocol. XMLP uses a CUPS instance on Linux or UNIX to act as the delivery server. Its straightforward to set up, check the developers guide for instructions.
You can configure the fax modems on CUPS with efax and FAX4CUPS. By the default setting, CUPS can fax the document in Postscript and PDF document formats.  We have not done much research on Fax over a Windows Server, there are commercial software packages that can use the IPP. Another alternative is to use one of the many Fax over Email solutions out there, we have several customers using it very sucessfully.


Sample FAXing code 

// create delivery manager instance 
DeliveryManager dm = new DeliveryManager();
// create a delivery request
DeliveryRequest req = dm.createRequest(DeliveryManager.TYPE_IPP_FAX);
// set IPP fax host
req.addProperty(DeliveryPropertyDefinitions.IPP_HOST, "myhost");
// set IPP fax port
req.addProperty(DeliveryPropertyDefinitions.IPP_PORT, "631");
// set IPP fax name
req.addProperty(DeliveryPropertyDefinitions.IPP_PRINTER_NAME, "/printers/myfax");
// set the document format
req.addProperty(DeliveryPropertyDefinitions.IPP_DOCUMENT_FORMAT, "application/postscript");
// set the phone number to send
req.addProperty(DeliveryPropertyDefinitions.IPP_PHONE_NUMBER, "9999999");
// set the document
req.setDocument("/document/invoice.pdf");
// submit the request
req.submit();
// close the request
req.close();

WebDAV
This delivery channel can direct your documents to any repository that supports the WebDAV protocol, whether that be Oracle Files Online, XDB or a third party solution. The document can be pushed into a directory on the repository as an archive or into a personal folder for viewing later by the recipient.
Again, its a simple case of setting properties and then calling the APIs submit method:

   // create delivery manager instance
     DeliveryManager dm = new DeliveryManager();
     // create a delivery request
     DeliveryRequest req = dm.createRequest(DeliveryManager.TYPE_WEBDAV);
      // set document content type
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_CONTENT_TYPE, "application/pdf");
     // set the WebDAV server hostname
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_HOST, "mywebdavhost");
     // set the WebDAV server port number
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_PORT, "80");
     // set the target remote directory
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_REMOTE_DIRECTORY, "/content/");
     // set the remote filename
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_REMOTE_FILENAME, "xdotest.pdf");
     // set username and password to access WebDAV server
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_USERNAME, "xdo");
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_PASSWORD, "xdo");
     // set the document
     req.setDocument("/document/test.pdf");
     // submit the request
     req.submit();

     // close the request
     req.close();


FTP
Another protocol that is very widely used to move files to specific locations for a daemon to recognize and have picked up by another process.

     // create delivery manager instance
     DeliveryManager dm = new DeliveryManager();
     // create a delivery request
     DeliveryRequest req = dm.createRequest(DeliveryManager.TYPE_FTP);

     // set hostname of the FTP server
     req.addProperty(DeliveryPropertyDefinitions.FTP_HOST, "myftphost");
     // set port# of the FTP server
     req.addProperty(DeliveryPropertyDefinitions.FTP_PORT, "21");
     // set username and password to access WebDAV server
     req.addProperty(DeliveryPropertyDefinitions.FTP_USERNAME, "xdo");
     req.addProperty(DeliveryPropertyDefinitions.FTP_PASSWORD, "xdo");
     // set the remote directory that you want to send your document to
     req.addProperty(DeliveryPropertyDefinitions.FTP_REMOTE_DIRECTORY, "pub");
     // set the remote file name
     req.addProperty(DeliveryPropertyDefinitions.FTP_REMOTE_FILENAME, "test.pdf");
     // set the document
     req.setDocument("/document/test.pdf");

     // submit the request
     req.submit();
     // close the request
     req.close();

HTTP
Finally, HTTP ... The Delivery API supports to deliver documents to the HTTP servers. The following sample is sending a document through HTTP POST method. Technically, you can send not only documents, but also anything you want. But the servers should be capable to accept your custom HTTP requests in advance, such as your custom Servlet or CGI program.

 // create delivery manager instance 
DeliveryManager dm = new DeliveryManager();
// create a delivery request
DeliveryRequest req = dm.createRequest(DeliveryManager.TYPE_HTTP);
// set request method
req.addProperty(DeliveryPropertyDefinitions.HTTP_METHOD, DeliveryPropertyDefinitions.HTTP_METHOD_POST);
// set document content type
req.addProperty(DeliveryPropertyDefinitions.HTTP_CONTENT_TYPE, "application/pdf");
// set the HTTP server hostname
req.addProperty(DeliveryPropertyDefinitions.HTTP_HOST, "myhost");
// set the HTTP server port number
req.addProperty(DeliveryPropertyDefinitions.HTTP_PORT, "80");
// set the target remote directory
req.addProperty(DeliveryPropertyDefinitions.HTTP_REMOTE_DIRECTORY, "/servlet/");
// set the remote filename (servlet class)
req.addProperty(DeliveryPropertyDefinitions.HTTP_REMOTE_FILENAME, "uploadDocument");
// set the document
req.setDocument("/document/test.pdf");
// submit the request
req.submit();
// close the request
req.close();
So thats the APIs and brief introduction to their use, some of you will have noticed a glaring exception to the list ... where the printing APIs?

They are present and use the IPP much like the fax. They are in my opinion superior to the current concurrent manager printing abilities but, I suspect that many of you have already been through the fun and games (NOT!) that is printer setup in Apps and have got them working ... it might not be quitre what you users want in the age of tray switching and duplex printing but if it ain't broke dont fix it! For those that are interested I'll cover printing another time. The other delivery channel I'll cover later is the custom channel, many of you will have thrid parties delivery software that you would like to hook up to the XMLP flow (if they have not done it for you), the custom channel can help.

Next week ... how to get these APIs running in the EBS and delivering your docs for you.

August 16, 2006

Document Delivery from EBS - Part III

Apologies for the break, trying to wrap up on some template standards for the EBS division ...


We now have an idea of the Delivery Manager APIs and what they can do and the limitations within the EBS infrastructure for using them to deliver documents. Now we need to know how we can hook up the delivery APIs to the concurrent manager. As I have mentioned before the delivery manager is not integrated into the concurrent manager. We need to use another method to get the document generated by the concurrent manager to the delivery manager. The concurrent manager expects to direct a document to a printer, the setup of printers has some flexibility and we can take advantage of that, rather than have a printer at the end of the flow we can have a shell script that can then direct the output to another delivery stream. 


Setup XMLP as a virtual printer


Using the System Administrator responsibility we can create a new Printer Driver but rather than calling a print command such as 'lp' we can call our shell script.


CM1:


The most important field is the 'Arguments' field, this is where we specify the script to call and pass parameters:


$XX_TOP/delivery/xmlpmailer.sh $PROFILES$.CONC_REQUEST_ID $PROFILES$.FILENAME + others


where
$XX_TOP/delivery/xmlpmailer.sh - is the location and name of the script we want to call
$PROFILES$.CONC_REQUEST_ID  - is the request id of the report we are sending
$PROFILES$.FILENAME - is the name of the file we want to deliver
others - we can pass other parameters, either from the PROFILES object or hardcode values here. There is no definitive list of what those PROFILES are in the Oracle documentation but using a little trial and error you can chekc what can and can not be passed. To find the other PROFILES that might be supported have the forms interface open Help > Diagnostics > Examine. Find PROFILES in the popup windows Block field. Heres what I have found is supported so far, not an exhaustive list:


$PROFILES$.CONC_REQUEST_ID
$PROFILES$.PRINTER
$PROFILES$.CONC_COPIES
$PROFILES$.TITLE
$PROFILES$.FILENAME
$PROFILES$.RESP_ID
$PROFILES$.USER_ID


With this lot we can derive quite alot of information and with a trip back to the database to we can get even more information.


To test all this theory you will need to enter the murky world of printers, printer types, styles and drivers ... I still sometimes struggle getting the right combination ... reading the manual, some trial and error and above all patience helps.
I created a very simple shell script to simply write the PROFILE values to a file:

echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 > /apps/vis11510appl/xdo/11.5.0/delivery/1.log
exit 0

The exit command is important, without it the concurrent manager will leave your request id as still running. Before you can test the new driver you'll need to bounce the concurrent manager and again for any changes. The following script became my best friend very quickly while putting these articles together:

startmgr sysmgr="apps/pwd" mgrname="std" printer="hqseq1" mailto="jsmith"
restart="N" logfile="mgrlog" sleep="90" pmon="5" quesiz="10"

So, we can call a script, but what do we want it do ?
Well by now you will hopefully have managed to get the printers on your network working with Apps and a great saying comes to mind here ... if it ain't broke don't fix it. If we leave printing then we have email, fax, ftp, webdav, http or even a custom channel to deliver documents to. All we need do is create a java class that takes the arguments above and then delivers the document ...easy!


Next article we'll tackle emailing documents from the EBS

August 17, 2006

Email from EBS

Trying to write this article for the third time today ... first time I forgot to commit it and my browser crashed ... serves me right for playing with IE7. Second time, I dont know what happened ... anyway trying again.


Apologies for the longer and longer titles ... hopefully the usefullness of the article outweighs the silly titles, got myself into a hole here. This time we're discussing emailing documents from the EBS, we need to pull together the Delivery Manager APIs with the shell scripts techniques we looked at last time.


But you can email already !


Little known fact but you can already allow your users to get emails from the concurrent manager. When submitting a request they can open the 'Options' popup and specify users that should receive a notification upon completion of the request. Thats great but how do they get hold of the output without logging back in and searching through the massive list of requests? If you set the 'Concurrent:Attach URL' to Yes then users will get a mail with a link in it to the output file. However, there are a couple of caveats:
1. The email can only be sent to a user registered in the system (remember to enter their email id)
2. The user has to be connected to the system to retrieve the output file.


Gotta thank Mr Azzopardi for this tidbit!

So what can I do?


To get around these limitations and attach an output file to an email to any user we can use the script print driver and the Delivery Manager APIs. Remember we can pass several parameters to the java via the script:


$PROFILES$.CONC_REQUEST_ID
$PROFILES$.PRINTER
$PROFILES$.CONC_COPIES
$PROFILES$.TITLE
$PROFILES$.FILENAME
$PROFILES$.RESP_ID
$PROFILES$.USER_ID


We could just add the email address we want to send the report to :

$PROFILES$.CONC_REQUEST_ID $PROFILES$.FILENAME tim.dexter@oracle.com

But that would mean either one person is going to get every report output from the system that uses that driver or you'll need to create a personal driver for every user, not a bad idea for smaller user groups. It would be better to derive the email address so we can have one email driver to maintain.
Using a combination of the PROFILES we can make a trip to the database to retrieve pretty much anything we want. With the USER_ID we can fetch the email address for that user; with the RESP_ID we could fetch all the emails for the users that have that responsibility. Using the CONC_REQUEST_ID we can get a hold of the concurrent program and maybe store the emails for the report in the Options field of the program definition form. We could even use the derived program id to look up emails against a custom table where we have stored users and programs ... we now have a nice report subscription model for EBS ... cool!


OK, let's do it ...


For this example this just tackle looking up the email address of the user ... the other options are just a variation on the same theme, its just getting the sql right.
So I have built the following java class:

package oracle.apps.xdo.ebsdelivery;

import oracle.apps.xdo.delivery.DeliveryManager;
import oracle.apps.xdo.delivery.DeliveryRequest;
import oracle.apps.xdo.delivery.DeliveryPropertyDefinitions;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

 

public class EBSEmailDelivery
{
  public EBSEmailDelivery(String rRequestID, String rFile, String rUser)
  {
        int userid =  Integer.parseInt(rUser);
        int requestid = Integer.parseInt(rRequestID);
   
    try {
        //get the report title
        String repTitle = getReportTitle(requestid);
        // create delivery manager instance
         DeliveryManager delMgr = new DeliveryManager();
        // create a delivery request
         DeliveryRequest delReq = delMgr.createRequest

(DeliveryManager.TYPE_SMTP_EMAIL);
        // set email subject
         delReq.addProperty(DeliveryPropertyDefinitions.SMTP_SUBJECT,

"EBS Report:"+repTitle +" for request: "+ rRequestID);
        // set SMTP server host
         delReq.addProperty(
         DeliveryPropertyDefinitions.SMTP_HOST, "mail.yourcompany.com");
        // set the sender email address
         delReq.addProperty(DeliveryPropertyDefinitions.SMTP_FROM,

"ebsadmin@oracle.com");
        // set the destination email address
         delReq.addProperty(DeliveryPropertyDefinitions.SMTP_TO_RECIPIENTS,

getEmail(userid) );
        // set the content type of the email body
         delReq.addProperty(DeliveryPropertyDefinitions.SMTP_CONTENT_TYPE,

"application/pdf");
        // set the document file name appeared in the email
         delReq.addProperty(DeliveryPropertyDefinitions.SMTP_CONTENT_FILENAME,

repTitle+rRequestID+".pdf");
        // set the document to deliver
         delReq.setDocument(rFile);
        // submit the request
         delReq.submit();
        // close the request
         delReq.close();

    }
    catch (Exception e) {
    e.printStackTrace();

    }
  }

  static Connection getConnection() throws SQLException, Exception
  {
        String fDriverName = "oracle.jdbc.driver.OracleDriver";
        String fDbName = "vis11510";
        String fServer = "xdodemo.us.oracle.com";
        String fPort = "1521";
        String fUserName = "apps";
        String fPassword = "apps";

        Class.forName(fDriverName).newInstance();
        Connection dbconn = DriverManager.getConnection
            ("jdbc:oracle:thin:@"+fServer+":"+fPort+":"

+fDbName, fUserName, fPassword);
        return dbconn;
  }
 
  private String getReportTitle(int requestID)
  {
    String reportName = "";
    try
    {
      // Try and geta connection to the db
      Connection conn = getConnection();
      // fetch the report name based on the request id
      PreparedStatement getTitle = conn.prepareStatement(

"select user_concurrent_program_name \n" +
"from fnd_concurrent_requests fcr,\n" +
 "fnd_concurrent_programs_vl fcpv\n" +
 "where fcr.concurrent_program_id = fcpv.concurrent_program_id\n" +
 "and request_id = ?");

//get the title
      getTitle.setInt(1,requestID);
    
      // get the query result in to a result set and then assign the
      // value to a variable we can pass back to the calling method
      ResultSet titleRslt = getTitle.executeQuery();
      titleRslt.next();
      reportName = titleRslt.getString(1);
      System.out.println(reportName);
     
      //Clean up
      titleRslt.close();
      getTitle.close();
      conn.close();
     
     
    }
    catch (SQLException eSQL)
    {
      System.err.println("Could not create connection");
      eSQL.printStackTrace();
    }
    catch (Exception e)
    {
     System.err.println("Exception thrown");
     e.printStackTrace();
    }
    
    return reportName;
  }
 
 
  private String getEmail(int userID)
  {
    String eMailID = "";
    try
    {
      // Try and geta connection to the db
      Connection conn = getConnection();
      PreparedStatement getEmail = conn.prepareStatement(

"select email_address from fnd_user where user_id = ?");
      getEmail.setInt(1,userID);
    
      // get the query result in to a result set and then assign the
      // value to a variable we can pass back to the calling method
      ResultSet emailRslt = getEmail.executeQuery();
      emailRslt.next();
      eMailID = emailRslt.getString(1);
      System.out.println(eMailID);
     
      //Clean up
      emailRslt.close();
      getEmail.close();
      conn.close();
     
     
    }
    catch (SQLException eSQL)
    {
      System.err.println("Could not create connection");
      eSQL.printStackTrace();
    }
    catch (Exception e)
    {
     System.err.println("Exception thrown");
     e.printStackTrace();
    }
         
    return eMailID;
  }
  public static final void main(final String[] args)
  {
    // Arguments passed
    //1.$PROFILES$.CONC_REQUEST_ID
    //2.$PROFILES$.FILENAME
    //3.$PROFILES$.USER_ID

    EBSEmailDelivery ebsMail = new EBSEmailDelivery(args[0], args[1], args[2]);

  }

}


You can get the source here.
Just a note, to develop and compile your class you are going to need the following libraries:
xdocore.jar - grab it from the Template Builder install
versioninfo.jar - same place
xmlparserv2-904.jar - the Oracle XML parser
mail.jar - this is the java mail library get it from Sun
Activation.jar - a supporting library for the mail library get it from Sun 

if you want to test it on your cleint machine you'll need the Oracle JDBC driver libraries too ... these will be under your EBS JAVA_TOP, jdbc12.zip worked for me. Ar runtime all the libraries will be available for you so all you'll need to do is add the path to your class in the shell script.

Walking through the class we have several methods.



  • EBSEmailDelivery - at the top we have the delivery manager APIs for emailing. We have seen these already. You'll need to add your outgoing email server.
  • getConnection() - this method gets us a connection to the db
  • getReportTitle - this fetches the report title based on the concurrent request id. Check out the query we're executing.
  • getEmail - this fetches the email of the user that ran the report based on the user id


The class is not catching every possible situation but you get the idea.

Now we have the class we mount it into the APPL_TOP. You should have a custom area under JAVA_TOP. My class is going to sit under the xdo directory under JAVA_TOP. You should note that the package name in the top of the class i.e. oracle.apps.xdo.ebsdelivery is going to define where the class is going to sit. In this case $JAVA_TOP/oracle/apps/xdo/ebsdelivery.

The shell script now needs to call the class and pass the parameters, I have the following:

CLASSPATH=/apps/vis11510comn/java/oracle/apps/xdo/ebsdelivery:$CLASSPATH
export CLASSPATH

java oracle.apps.xdo.ebsdelivery.EBSEmailDelivery $1 $2 $3
RESULT=$?
if [ $RESULT -ne 0 ]
then
echo "`date` There was an error while delivering the document."
echo "`date` Please inform your System Administrator."
exit $RESULT
fi

# Return success code to Oracle concurrent manager
exit 0


So there you have it, your users can get emails from the EBS with their reports attached. Of course you can get very sophisticated and grab multiple email addresses or even multiple reports if you are using a request set ... just let your imagination run riot.

Next ... faxing documents

August 24, 2006

Email from EBS - Addendum

The fax article is on the way but since I came up with the email solution there has always been the niggling fact that I was putting the EBS user name/password openly into my java class ... there had to be a better a solution !


There is, and thanks to Ashish from the dev team and a little work I found it. The OA Framework UI uses a DBC file to define the connection and other information to the EBS database and we can use that to get a connection to the database instead of hard coding the connection.


The file resides under the $FND_TOP/secure directory, check it out you'll have one. It will look similar to this:

#DB Settings
#Sun Sep 04 13:02:20 PDT 2005
GUEST_USER_PWD=user/pwd
APPL_SERVER_ID=F936BF325610DB94E03023823E050DC825406294681422881503890572956133
FND_JDBC_BUFFER_DECAY_INTERVAL=300
APPS_JDBC_DRIVER_TYPE=THIN
FND_JDBC_BUFFER_MIN=1
GWYUID=user/pwd
FND_JDBC_BUFFER_MAX=5
APPS_JDBC_URL=jdbc:oracle:thin:@(DESCRIPTION=(LOAD_BALANCE=YES)
(FAILOVER=YES)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)
(HOST=your server)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=sid)))
FND_JDBC_STMT_CACHE_FREE_MEM=TRUE
FND_JDBC_STMT_CACHE_SIZE=200
TWO_TASK=yourdb
FND_MAX_JDBC_CONNECTIONS=500
FND_JDBC_USABLE_CHECK=false
FNDNAM=user
FND_JDBC_PLSQL_RESET=false
DB_PORT=1521
FND_JDBC_CONTEXT_CHECK=true
FND_JDBC_BUFFER_DECAY_SIZE=5
DB_HOST=your server

there is also an environment variable called $FND_SECURE which will point to the file location.
To take advantage of the file and its contents and make your java class a whole lot more secure you can pass the FND_SECURE value and the dbc file name to the class via the driver i.e. $FND_SECURE/file.dbc


Then in your class add the following:


import oracle.apps.fnd.common.AppsContext;
...

the getConnection method becomes very simple
  static Connection getConnection() throws SQLException, Exception
  {
       
        AppsContext aCont = new AppsContext(dbcLocation);
        Connection aConn  = aCont.getJDBCConnection();
        return (aConn);
       
  }


where dbcLocation is a string that holds the new parameter passed from the driver. The rest of the class remains the same.
So now you have a much more secure method to get the connection. Just be sure to close it after you have finished so the connection returns to the connection pool. Much cleaner, neater and faster :o)




 


 

August 25, 2006

Faxing from EBS

Fax integration is probably one of the most popular and tougher delivery destinations from the EBS, you need connections to the fax server from EBS, you need to be able to convert your documents to a tif format, you may need very high volumes of faxes, your users will want to be able to monitor the delivery and have automatic redial if the fax recipient's machine is busy. This has required third parties and Oracle Partners to provide functionality for faxing documents.
The XMLP faxing solution aleviates some of these issues but it is not a replacement for some of the fine solutions out there.


What can it do?


  • XMLP can send a fax via the IPP protocol, this requires a delivery server such as CUPS to handle the fax the request.
  • XMLP can be integrated into the concurrent manager or even a specific product
  • XMLP can be used for lightweight faxing requirements
  • You can build a complete faxing solution using the XMLP APIs
What can't it do?



  • It can not handle huge volumes of faxing, Im talking thousand of faxed an hour
  • No out of the box redial and monitoring EBS UI. CUPS has its own UI that could be used.

If you're still interested in building a fax delivery channel then you can use the java APIs and the shell script in conjunction with a dummy printer driver. Again, you can extend your implementation as much as you want so you can build feedback into the concurrent manager or even build your own fax monitoring UI interface.

For this demo we'll take a similar approachto that of the email solution. We'll need a CUPS server set up to communicate the fax content to. CUPS is only for UNIX or LINUX implementations, for those of you on a Windows there are commercial solutions available to have a FAX machine connected to your network via IPP. We will also use the database querying to grab the fax number.

Step1: Set up CUPS


The set up of CUPS for faxing is covered in the user guide (pg 11-36) you'll need some extra packages and install them. The good news is that they are all open source solutions and therefore inexpensive, the not so good news is that support is going to be patchy. Follow the instructions and test that the fax machine is sending output when called.

Step 2: Build the java class

We can reuse much of the email code we wrote last time. We of course need to use the fax APIs rather than the Email. We also need to fetch the fax number of course, you can use the techniques we used last. For this example I decided to try something different. Im assuming that the report that is to be delivered actually has the fax number embedded inside it. In this case we can fetch the XML data used to create the report, parse it and retrieve the fax number value.
I have been a little lazy with my class, it parses and sends the document, if you were going to use the parser functionality across multiple delivery classes then you would break it out on its own.
I use the SAXParser rather than the DOM, we're not sure how much data we are going to get and do not want to gobble up memory loading the whole document using the DOM parser.
In the class I have I just have a few methods to:


  1. Parse the document - the parser just moves through the document and event are fired that we can subscribe to.
  2. Look for the specific element that holds the fax number - once found its sets a flag for the next method to
  3. Fetch the fax number value into a variable and sets another 'found the element' flag for the next function
  4. Check the closing element that holds the fax number - if the flag is set then the parser stops working through the document.


Heres the code snippet for the parser, the startElement, endElement and characters methods are standard SAXParser methods we need to implement:


  public String parseFAXNum (String xmlFile)
  {
    try {
         
          SAXParser parser = new SAXParser();
//Because Im lazy my class is extending the DefaultHandler for SAX
// so I can just use 'this' for the following methods



          parser.setContentHandler(this);
          parser.setErrorHandler(this);
          // We parse the XML file fetched from the variables passed
// from the printer driver
          parser.parse(xmlFile);
         
    }
          catch (Exception ex) {
                System.out.println(ex);
          }
        return(xFaxNum);
  }
 
  public void startElement(String uri, String localName,
          String rawName, Attributes attributes) 
          // We're checking for the CUST_FAX_NUM element, this method
// looks for the opening tag. If found then we set a
// "found" flag to true for the "characters" method

       {
             if (localName.equals("CUST_FAX_NUM"))
             {
                getEleVal = true;
              }
       }
  public void endElement (String uri, String localName,
          String rawName) throws SAXException
          {
         // We're checking for the CUST_FAX_NUM element, this method looks
// for the closing tag.
         // If found then we throw an exception to stop the parser.
            if (localName.equals("CUST_FAX_NUM"))
            {
               getEleVal = false;
               throw new SAXException("Found element required");
             }
          }
    public void characters(char[] cbuf, int start, int len) 
      {
         if (getEleVal)
         {
           // We have the value we want so assign it to the fax variable
           xFaxNum = new String(cbuf,start,len);
           
         }
      }
If you wanted to break the parser out on its own and use it for multiple report output you could place the logic in the code to check the root of XML ie RAXINV for the Invoice report, POXRPOP for Purchase Orders and then find the appropriate element for the fax number.

Once we have the fax number we're pretty much set to call the appropriate APIs to deliver the document.
    try
    {
      // fetch the fax number from the XML data that is
      // fetched via the requestid from the driver
      String faxx  = parseFAXNum(getXMLFile(requestid));
      // create delivery manager instance
      DeliveryManager dm = new DeliveryManager();
      // create a delivery request
      DeliveryRequest req = dm.createRequest(DeliveryManager.TYPE_IPP_FAX);
      // set IPP fax host - this is our CUPS server
      req.addProperty(DeliveryPropertyDefinitions.IPP_HOST,
"xdodev2.us.oracle.com");
      // set IPP fax port - - this is our CUPS server port
      req.addProperty(DeliveryPropertyDefinitions.IPP_PORT, "631");
      // set IPP fax name - this is our CUPS fax name
      req.addProperty(DeliveryPropertyDefinitions.IPP_PRINTER_NAME,
"/printers/fax1");
      // set the document format
      // we can pass a pdf document and CUPS will convert it to tiff for us
      req.addProperty(DeliveryPropertyDefinitions.IPP_DOCUMENT_FORMAT,
"application/pdf");
      // set the phone number to send
      req.addProperty(DeliveryPropertyDefinitions.IPP_PHONE_NUMBER, faxx);
      // set the document
      req.setDocument(rFile);
      // submit the request
      req.submit();
      // close the request
      req.close();
    }

You might have noticed that we are  passing a pdf format to the CUPS server, CUPS will convert the pdf to tiff format for us. You can see the complete class definition here.


Now all you need do is create a new fax "printer" driver to call your class and you now have faxing from the EBS with some level of flexibility for your users.
If you are looking at handling a batch of documents, XMLP has a much better solution in its Bursting Engine and I'll cover that once I have completed the delivery articles.


Happy Faxing!

August 28, 2006

Document Storage and EBS

Couple more delivery channels to cover and we'll have exhausted the options for document delivery from EBS. For this article Im turning to document management. Many of you will have a document management system in your company this may be as simple as scanning invoice documents and storing them in some LOBS table in the EBS schema for later retrieval or you may have Oracle Files or maybe a third party solution.
To get the documents to the repository may involve some third party or custom code interfacing into the EBS stack. XML Publisher can help you get the documents to the repository directly from the concurrent manager. Whats more you can do this while you are delivering the document to the end user at the same time and even do some post processing to maybe add a watermark to the archived document with the word "COPY" striped across it.


Same again please ...


With all the work we have done with email and faxing we now know how the whole solution hangs together and how we can call APIs to get the documents delivered. All we need do is add the code to send the document(s) to the correct place in the repository. Just about all the document management solutions offer a webdav channel to push documents into the document repositiory. They may also have the ability to push in some metadata about the document. I have naturally picked on Oracle Files in the example below, but its still generic for all webdav servers.

    /*create delivery manager instance*/
     DeliveryManager dm = new DeliveryManager();
     // create a delivery request
     DeliveryRequest req = dm.createRequest(DeliveryManager.TYPE_WEBDAV);
      // set document content type
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_CONTENT_TYPE,
"application/pdf");
     // set the WebDAV server hostname
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_HOST, "mywebdavhost");
     // set the WebDAV server port number
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_PORT, "80");
     // set the target remote directory
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_REMOTE_DIRECTORY,
"/content/");
     // set the remote filename
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_REMOTE_FILENAME,
"xdotest.pdf");
     // set username and password to access WebDAV server
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_USERNAME, "xdo");
     req.addProperty(DeliveryPropertyDefinitions.WEBDAV_PASSWORD, "xdo");
     // set the document
     req.setDocument("/document/test.pdf");
     // submit the request
     req.submit();
 
     // close the request
     req.close();

I want to watermark ...

To add a watermark to the document its just a simple API call.

 // You already have the document locally so call the PDFDoc merger
// and create input and output streams for it
PDFDocMerger docMerger = new PDFDocMerger(inputStreams, outputStream);
// You can use setTextDefaultWatermark() without these detailed setting
docMerger.setTextWatermark("COPY", 200f, 200f); //set text and place
docMerger.setTextWatermarkAngle(80); //set angle
docMerger.setTextWatermarkColor(1.0f, 0.3f, 0.5f); // set RGB Color
// Merge PDF Documents and generates new PDF Document
docMerger.mergePDFDocs();

Pretty straightforward right?
There are a couple of points here:
1.There is a need to get the username/pwd for the repository. There are ways around this, putting documents into a public directory, passing the username/pwd from the EBS instance ... Im not going to cover this in this article thou.
2. Metadata - all of the repositories I have come across have a java interface to push meta data in. You could use the XML parsing techniques in the fax article to grab pertinent information about the document to join it in the repository.


Short but sweet ... next, custom channels

April 17, 2007

Adding OMR Marks

We had a question come up on an internal mailing list yesterday that got me thinking. Can BIP/XMLP add OMR marks to documents? For the uninitiated, OMR, or Optical Mark Recognition, in one of its simpler forms is the series of 'slugs' (technical term) you see on the right hand side of your paper phone bill. They are short 1/2 inch (1.2cm ... sorry Europe Im all inches now :) lines that tell a paper folding machine where to fold the paper prior to sticking the sheets into an envelope.

This is not the only application of OMR, the routing and account numbers on checks are of the same ilk i.e. machine readable. I have covered checks in other postings and there is a white paper on the BIP OTN pages. Those of you in Europe and maybe the rest of the world that have taken 'multiple guess', sorry 'multiple choice' (MC) exam papers and have had to fill in the appropriate ellipse shape corresponding to what you think is the correct answer are using OMR technology.
I always thought these were the easiest exams until I got to university. My Applied Chemistry professor announced to a greatly relieved class that our mid and final term papers would be in a multiple choice format ... fan-tas-tic! Oh how wrong we were! My experience of MC thus far had been of the four choices, you could throw out one as being completely ridiculous. As long as you knew something on the subject, another two were 'dodgy' so that left the correct answer - easy right. Our professor put a twist on the format:


  • We had 200 questions
  • 5 choices not 4 - none of which were obviously wrong
  • Get a question incorrect and you lost 2 points, dont answer and lose 1 point - this was done to put off the student who hedged their bets and relied on the 20% chance that they would get questions right if they picked ABCDEABCDEABCDE .... we had some real high achievers in our classes :o)
Maybe it was not quite that nasty ... still it gave you huge incentive to learn the material and we covered so much information in the two hours a week we had with her that your hand would be cramping by the time class finished or you'd be begging classmates for a copy of their unintelligible notes. Anyway, I passed ... lesson learned, 'if you see MC be waryee' a mantra I have lived by since.

Slugging Options


Anyway, I digress ... badly. So, just how can you add those marks to your output? 
There are commercial OMR fonts out on the market but for the purposes of marking paper for folding we have found that you can either use the '-' or '_' characters or a simple line drawing to creat the slug. All of the 'folding' manufacturers publish the dimensions of the the slug they need so its just a case of changing the font size or the line thickness/length to the desired size. There are a couple of options both work but it depends on your requirements.
a. Some machines just require marks to tell it where to fold, maybe 2 or 3 per page or
b. Requirements are more sophisticated, checksum slugs that need to appear only on the last page or on eavery X number of pages.
Options:


  • Use line drawings - I have covered elsewhere how you can manipulate drawings in the RTF templates. You can use the same technique to create the necessary 'slugs' and then repeat them down the page on every page unless you put in a section break. This method can and does work for 'a'
  • Use PDF APIs - assume your PDF has been generated. You can now use the PDF APIs, taking advatage of the watermarking method you can add the slug to specific x,y positions on particular pages. We have customers using this method combined with the page counting API and the bursting engine.

    1. Burst a set of documents and subscribe to the BurstListener,
    2. as the document is generated, grab it, count the pages - FormProcessor.getPageNumber() 
    3. calculate where and then apply the 'slugs' to each page appropriately - PDFDocMerger.setTextXXXX - where XXXX are the methods to set text, font and position. 
    4. deliver document to paper folder

You do not have to use the bursting engine to add the marks. You can add them to any PDF document, you could create a printer driver that called the API prior to sending the document to the printer so you do not have to interrupt or meddle with the concurrent manager - I covered how to do this in the EBS Delivery series.

Of the two, 1 is simpler but of course only meets the simpler requirements. Option 2 requires java (simple java mind you) and a little custom work but its well within your means.
Happy Slugging!

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.


 

September 17, 2007

Printing Garbage

Many of you know that publisher requires some 'extras' to get your document to a printer in a legible format. There are now printers that can handle the PDF format natively but the majority of printers need to have the PDf converted to either Postscript (PS) or Printer Control Language(PCL). We have recommended a couple of converters to help you out with this.


Ghostscript, offers both PS and PCL converters the other product we recommend is xpdf from foolabs and we have many of you using both products very successfully. In spite of their robustness many of you have expressed concern - 'it's another moving part in the whole document generation process' - 'and it's not owned by Oracle' -  both fair comments and I can understand the concern - however we have faith in these solutions and will help you to use them.


For BIP Standlaone users there is some good news, we have now built our own Postscript libraries to convert from PDF to PS. Very easy to use when setting up your printers in the user inteface.


As you can see its simple to use and if you want to continue to use another converter thats simple too. There are some caveats and limitations:


This release of the PDF to PostScript converter has the following limitations:




  • Only PDF version 1.4 or earlier is supported. This is also the currently supported version of the Oracle BI Publisher PDF output.



  • CID fonts, which are used mainly to support languages with large sets of glyphs like Chinese, Japanese, and Korean, are only supported when embedded in the PDF.



  • Transparent and half-tone renderings are not supported.



  • DeviceN color space is not supported.



  • Shading patterns are not fully supported.



  • Vertical writing is not supported.


Not forgetting you EBS and other apps users out there - this functionality will be coming to an application near you later this year (for EBS) and next year for others.


 

October 3, 2007

Document Delivery From EBS ? Part IV

A treat for me today, Brent Lowe from Oracle Partner STR Software sent me a blog entry to share. STR Software provide a really nice, tight delivery server integration for EBS customers. They have developed a user interface to allow you to deliver and monitor documents via the forms interface. If you are not wanting to get into the delivery manager APIs and need a 'delivery' solution that installs in a snap and works with Publisher check out STR's offereing.


Brent's post deals with a delivery channel I did not cover in last year's delivery series - that is the 'custom' channel. He uses the extensibility of the Publisher delivery manager to provide the tight integration that you see in the STR solution today. This was just what we were looking for from a third party when we decided to provide the custom channel option. If you are wondering how you could take advantage of the channel, read on ... if not read on anyway its a good article.


So with out further ado ...


Document Delivery From EBS : Part IV



Brent Lowe


To continue the series on document delivery from EBS, I thought it would be helpful to demonstrate the use of a Custom Delivery Channel.  Looking back on Tim痴 original post, the Custom Delivery Channel was defined as:


"Custom" if you have your own channel or a third party delivery solution you (or they) can create a custom delivery channel interface and XMLP will direct documents down that channel.?


As it turns out, we (STR Software) fall into this realm of the 奏hird party delivery solution? and decided to extend the BI/XML Publisher Delivery Manager email and fax capabilities by creating our own Custom Channel.   The AventX Custom Channel enables a number of features we felt were necessary to provide in an enterprise environment:



  • Centralized management of email and fax transmissions.
  • Notification of the successful or failed transmission of documents to email addresses, flat files or via reports.
  • Multiple email and fax recipients or combinations of both.
  • Support of multiple attachments or multiple documents in a single transmission.
  • Dynamic cover page information such as the recipient痴 name and company, send information, remarks or notes, etc.
  • Does not use open source/unsupported software (CUPS/eFax/FAX4CUPS).

OK, enough about us, let痴 get back to the real point of this post. 


Once you have a Custom Delivery Channel, how do you use it?


To demonstrate how to use a Custom Delivery Channel, I thought it would be beneficial to walk through an example of delivering a standard Purchase Order (or any document) from Oracle EBS. 


Step 1.   Create a class that utilizes the Custom Channel.


There are a couple key points to remember here.



  • You must register your Custom Channel prior to using it.
  • You must import the properties of your Custom Channel so that they are available for use.

So how do you register your Custom Channel?  You have 2 options.



  1. Use the $XDO_TOP/resource/xdodelivery.cfg file

The xdodelivery.cfg file has a section named <channels>, here you can simply define the location of your custom channel痴 Request Factory and it will be made available for the entire Delivery System.


For example:


<channel name="aventx">com.strsoftware.axtko.delivery.aventx.AventXDeliveryRequestFactory</channel>



  1. Register the Custom Channel inline in the java code itself by simply adding the call:

DeliveryManager.addRequestFactory("aventx",
"com.strsoftware.axtko.delivery.aventx.AventXDeliveryRequestFactory");


Importing the properties of your custom channel is equally easy; just add an import statement in your source:

import com.strsoftware.axtko.delivery.aventx.AventXPropertyDefinitions;

Now that you have registered your Custom Channel and imported the properties, using the channel is as easy as setting the appropriate properties to the values that are important to you, your users and the recipients.  To see all of the available properties in our custom channel, check out this link.   In the example below I am setting the following properties:



  • The email or fax cover page subject to be "Purchase Order #x" where x is the actual PO number.
  • The Recipient Name and Company information for a fax cover page or email message body.
  • The Sender痴 name and email address for the fax cover page or for email from information and bounce backs.
  • Multiple email confirmations to be delivered to the user that submitted the document, the buyer of the PO and a hard coded address.  These notifications will be delivered on the Successful or Failed transmission of the email or fax.
  • The Concurrent Request ID, Oracle User Name, Organization ID and Document ID so that the document can be easily queried or reported on.
  • Different attachments to my delivery, in this case I am adding a description of one of the widgets I am purchasing and my standard terms and conditions.
  • The name of my Purchase Order attachment to be 撤urchase Order #X.pdf? for email delivery.
  • Remarks for my cover page and email message body.  Note that for faxes I can actually add a Signature (denoted by the [SIG=]) to my cover page!
  • A byte delimited status file that can be read programmatically to post status information back to my Oracle database.
  • And finally, I set who I want this document to go to, where sDestination can be a fax or email address.
         // Create an 'aventx' delivery channel
         DeliveryManager.addRequestFactory("aventx",
"com.strsoftware.axtko.delivery.aventx.AventXDeliveryRequestFactory");
         DeliveryManager deliverymanager = new DeliveryManager();
DeliveryRequest deliveryrequest = deliverymanager.createRequest("aventx");
         // Add specific properties about this document.
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_SUBJECT, "Purchase Order #" + sDocId);
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_TOCOMPANY, sToCompany);
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_TONAME, sToName);
        deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_FROMNAME, sUserName);  
        deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_FROMEMAIL, sUserEmail);
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_CONFIRMEMAIL1, sUserEmail);
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_CONFIRMLEVEL1, "All");
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_CONFIRMEMAIL2, sBuyerEmail);
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_CONFIRMLEVEL2, "Success");
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_CONFIRMEMAIL3, "purchasing@yourcompany.com");
       deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_CONFIRMLEVEL3, "Fatal");
deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_ERPTRACKNO, sRequestId);
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_ERPLOGIN, sUser);
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_DOCID, sDocId);
         deliveryrequest.addProperty(AventXPropertyDefinitions.
AVENTX_ORGID, sOrgId);
         deliveryrequest.addProperty(AventXPropertyDefinitions.AVENTX_SECONDARYATTACHMENTFILE1,
"\tmp\Instructions.doc");
         deliveryrequest.addProperty(AventXPropertyDefinitions.AVENTX_SECONDARYATTACHMENTNAME1,
"Instructions_For_Vendor.doc");
         deliveryrequest.addProperty(AventXPropertyDefinitions.AVENTX_SECONDARYATTACHMENTFILE2,
"\tmp\T&C.doc");
         deliveryrequest.addProperty(AventXPropertyDefinitions.AVENTX_SECONDARYATTACHMENTNAME2,
"Terms_and_Conditions.doc");
         deliveryrequest.addProperty(AventXPropertyDefinitions.AVENTX_PRIMARYATTACHMENTNAME,
"Purchase_Order_#" + sDocId + ".pdf");      
         // Create the remarks for the coverpage.
         sRemarks =  "Dear Sir or Madam, \nPlease find the attached Purchase Order #" + sDocId + ".\n" ;
         sRemarks += "It has been a pleasure working with you!\n\n";
         sRemarks += "Sincerely,\n";
         sRemarks += "[SIG=RBL1]\n\n\n\n";
         sRemarks += "Brent Lowe";

deliveryrequest.addProperty(AventXPropertyDefinitions.AVENTX_REMARKS, sRemarks);
         deliveryrequest.addProperty(AventXPropertyDefinitions.AVENTX_ACKFILE,
"$FCHOME/tmp/grimlock.VIS1.^FAXID.%X.ACK");
         deliveryrequest.addProperty(AventXPropertyDefinitions.AVENTX_DESTINATION, sDestination);
         // Set the actual document to deliver.
         deliveryrequest.setDocument(sFileName);
         // Submit the document.
         deliveryrequest.submit();
         deliveryrequest.close();
         ...

To see a sample of the delivered document -> fax  |  email


Step 2.   Create a print driver in Oracle to call your class


Following Tim痴 example in Part III and Emailing From EBS, we can setup XMLP as a virtual printer and have that printer call our class:

   CLASSPATH=$FCHOME/axtko:$FCHOME/axtko/com/strsoftware/axtko/samples:$CLASSPATH
   export CLASSPATH
   java AventXDeliverySample $*
   RESULT=$?
   if [ $RESULT 墨e 0 ]; then
      echo 典here was an error while delivering the document.?
      exit $RESULT
   fi
   exit 0

Now when running your Purchase Order Reports, you can simply direct the output to your XMLP Virtual printer!


As you can see, using a Custom Delivery Channel is as easy as using the standard fax and email APIs.  What a Custom Channel buys you over the base channels is flexibility.   As you値l note from the above example, I have far more control over my document delivery needs as I am able to tailor the channel to my business specifications.

February 29, 2008

Use your CUPS

The end of another hectic week - I have been on so many customer calls I have lost count - but they have raised some good questions which I can turn into blog articles so there were some good benefits for all parties. One such was this morning. Those of you using the standalone/BIEE release will know that we communicate with printers via Internet Printing Protocol (IPP) - typically using CUPS or a windows print server. Not news anymore but so many of you have your printers set up as network printers and they work  - so that old adage applies, 'if it ain't broke don't fix it'.


I was talking to just such a customer this morning, they have installed BIP on its own server but were concerned about installing and administering CUPS - they were not allowed root access to the server so if CUPS required it to administer printers, it would be a showstopper!
Kei, from our development team to the rescue ... you can configure the CUPS server to use a non-root enabled user to administer the printers and fax servers.



  1. Create an OS user 'guest', set the password.
  2. Open cupsd.conf and go to <Location /admin> section
  3. Add a line "Require user guest" in there.
  4. Restart the CUPS server

Here's the detail.
http://www.cups.org/documentation.php/ref-cupsd-conf.html


and whats required in the cups.conf file

--- cupsd.conf

<Location /admin>
#
# You definitely will want to limit access to the administration functions.
# The default configuration requires a local connection from a user who
# is a member of the system group to do any admin tasks.  You can change
# the group name using the SystemGroup directive.
#
AuthType Basic
#AuthClass System
#AuthClass User
Require user guest


Cooll stuff this CUPS!

July 14, 2009

Now fax your documents through RightFax server!!

Good news for all EBS BI Publisher enthusiasts! Now you can deliver faxes directly from the BI Publisher Delivery
Manager to any fax destination through RightFax 9.3 or above. To learn more about RightFax you can visit their
website - http://www.captaris.com/rightfax.

Oracle Collections team has implemented BI Publisher - RightFax integration for Strategy Dunning Program. You can find the details of this implementation and patch download option in metalink (patch details at the end).

BI Publisher Delivery Manager can now make use of RightFax XML interface to submit a fax job and to query the
status of the job. These XML requests and responses are transmitted over HTTP/S. RightFax can convert the XML request into FCL (Facsimile Command Language) on the server side. So we do not have to worry about embedding FCL code on client (BI Publisher) side anymore. RightFax supports PDF, Postscript and PCL documents and may also support Microsoft Office documents depending on its Server configuration. The Delivery Manager supports a single fax destination per delivery request; therefore, to send fax to multiple destinations, you will have to submit multiple delivery jobs.

Here are the steps to set up the RightFax Delivery Channel in BI Publisher Delivery Manager

  1. Create a Delivery Manager instance
  2. Create a Delivery Request instance
  3. Add the request properties
  4. Set your document to the DeliveryRequest instance
  5. Submit the delivery request

And here is a sample code.....

  // create delivery manager instance
  DeliveryManager  dm = new DeliveryManager();
  // create a delivery request
  DeliveryRequest  req = dm.createRequest(DeliveryManager.TYPE_RIGHTFAX);
  // required host information (hostname / IP address / reference name from config file)
  req.addProperty(DeliveryPropertyDefinitions.RIGHTFAX_HTTP_HOST,"myhost");

  // required sender information
  req.addProperty(DeliveryPropertyDefinitions.RIGHTFAX_SENDER_RFUSER,"Administrator");
  // Optional properties
  req.addProperty(DeliveryPropertyDefinitions.RIGHTFAX_SENDER_FROM_NAME,"Dave Taylor");
  req.addProperty(DeliveryPropertyDefinitions.RIGHTFAX_SENDER_FROM_COMPANY,"ABC, Ltd.");
  // required destination
  req.addProperty(DeliveryPropertyDefinitions.RIGHTFAX_FAX_TO_NUMBER,"555-1111");
  // Optional properties
  req.addProperty(DeliveryPropertyDefinitions.RIGHTFAX_FAX_TO_NAME,"Fred Flintstone");
  req.addProperty(DeliveryPropertyDefinitions.RIGHTFAX_FAX_TO_COMPANY,"Acme, Inc.");
  // set the document
  req.setDocument("/document/invoice.pdf");
  // submit the request
  req.submit();
  // close the request
  req.close();

Did you say what all RightFax Delivery Properties?

Ok, so here goes a long list of delivery properties supported for RightFax delivery channel. It shows the corresponding RightFax XML interface element for submitting a fax job. Here RIGHTFAX_HTTP_HOST, RIGHTFAX_SENDER_RFUSER, RIGHTFAX_FAX_TO_NUMBER are the only required properties, rest are optional.


Delivery Property

Description

Rightfax XML
interface element
for submitting
a fax job

RIGHTFAX_HTTP_HOST

Required.
HTTP host of the RightFax server.

 

RIGHTFAX_HTTP_PORT

Optional.
HTTP port of the RightFax server.
Default=80.

 

RIGHTFAX_HTTP_REMOTE_DIRECTORY

Optional.
Enter the remote directory name.
Default value is “/rfxml”.

 

RIGHTFAX_HTTP_REMOTE_FILENAME

Optional.
Enter the remote filename.
Default is “/RFWebCon.dll".

 

RIGHTFAX_HTTP_AUTHTYPE

Optional.
HTTP authentication type of the
RightFax server url. Valid values are
RIGHTFAX_HTTP_AUTHTYPE_NONE,
RIGHTFAX_HTTP_AUTHTYPE_BASIC,
RIGHTFAX_HTTP_AUTHTYPE_DIGEST.
Default value is
RIGHTFAX_AUTHTYPE_NONE.

 

RIGHTFAX_HTTP_USERNAME

Optional.
HTTP username for the RightFax
server url. Required when
RIGHTFAX_HTTP_AUTH_TYPE is set to
values other than
RIGHTFAX_HTTP_AUTHTYPE_NONE.

 

RIGHTFAX_HTTP_PASSWORD

Optional.
HTTP password for the RightFax server
url. Required when
RIGHTFAX_HTTP_AUTH_TYPE is set to
values other than
RIGHTFAX_HTTP_AUTHTYPE_NONE.

 

RIGHTFAX_HTTP_ENCTYPE

Optional.
The encryption type can be set to
either of the following:
RIGHTFAX_HTTP_ENCTYPE_NONE
– no encryption (default)
RIGHTFAX_HTTP_ENCTYPE_SSL
– use Secure Socket Layer

 

RIGHTFAX_HTTP_USE_FULL_URL

Optional.
Set to “true” to send the full URL
for the HTTP request header. Valid
values are “true” or “false” (default).

 

RIGHTFAX_HTTP_TIMEOUT

Optional.
Enter a length of time in milli-
seconds after which to terminate the
request if a connection is not made
to the HTTP server.
The default is 60000 (1 minute).

 

RIGHTFAX_HTTP_PROXY_HOST

Optional.
Enter the proxy server host name.

 

RIGHTFAX_HTTP_PROXY_PORT

Optional.
Enter the proxy server port number.
Default=80.

 

RIGHTFAX_HTTP_PROXY_AUTHTYPE

Optional.
Valid value is either of the following.
RIGHTFAX_HTTP_PROXY_AUTHTYPE_NONE
– no authentication
RIGHTFAX_HTTP_PROXY_AUTHTYPE_BASIC
– Use HTTP basic authentication
RIGHTFAX_HTTP_PROXY_AUTHTYPE_DIGEST
– Use HTTP digest authentication.

 

RIGHTFAX_HTTP_PROXY_USERNAME

Optional.
Enter the username for proxy
authentication.

 

RIGHTFAX_HTTP_PROXY_PASSWORD

Optional.
Enter the password for HTTP proxy
authentication.

 

RIGHTFAX_SENDER_FROM_NAME

Optional.
Enter the name of the sender.

SENDER/FROM_NAME

RIGHTFAX_SENDER_EMPID

Optional.
Enter the employee id of the sender.

SENDER/EMP_ID

RIGHTFAX_SENDER_FROM_COMPANY

Optional.
Enter the name of the sender’s company.

SENDER/FROM_COMPANY

RIGHTFAX_SENDER_FROM_DEPARTMENT

Optional.
Enter the name of the sender’s
department.

SENDER/FROM_DEPARTMENT

RIGHTFAX_SENDER_FROM_PHONE

Optional.
Enter sender’s phone number.

SENDER/FROM_PHONE

RIGHTFAX_SENDER_RETURN_EMAIL

Optional.
Enter sender’s return email address.

SENDER/RETURN_EMAIL

RIGHTFAX_SENDER_BILLINFO1

Optional.
Enter the billing code of the fax
owner.

SENDER/BILLINFO1

RIGHTFAX_SENDER_BILLINFO2

Optional.
Enter the secondary billing code
of the fax owner.

SENDER/BILLINFO2

RIGHTFAX_SENDER_RFUSER

Required.
Enter the name of the sender’s
RightFax user name.

SENDER/RF_USER

RIGHTFAX_FAX_TO_NUMBER

Required.
Enter the fax number where the
document will be sent.

DESTINATIONS/FAX/TO_FAXNUM

RIGHTFAX_FAX_TO_NAME

Optional.
Enter the recipient’s name.

DESTINATIONS/FAX/TO_NAME

RIGHTFAX_FAX_TO_COMPANY

Optional.
Enter the recipient’s company name.

DESTINATIONS/FAX/TO_COMPANY

RIGHTFAX_FAX_TO_ALTNUMBER

Optional.
Enter the alternative fax number.

DESTINATIONS/FAX/ALT_FAX_
NUM

RIGHTFAX_FAX_TO_CONTACTNUMBER

Optional.
Enter the contact phone number of
the recipient.

DESTINATIONS/FAX/TO_
CONTACTNUM

RIGHTFAX_FAX_COVERSHEET

Optional.
Enter the coversheet template for
the current document. The file name
can be either a full path on the
RightFax server machine or a path
relative to RightFax\Production\Covers.

DESTINATIONS/FAX/COVERSHEET

RIGHTFAX_COVERTEXT

Optional.
Enter the text that should appear on
the coversheet.

COVERTEXT

RIGHTFAX_COVERTEXT_TYPE

Optional.
Enter the type of the coversheet
text. TXT (default) or RTF

COVERTEXT/type

RIGHTFAX_COVERTEXT_ENCODING

Optional.
Enter the encoding of the coversheet
text. NONE (default) BASE64
QUOTEDPRINTABLE

COVERTEXT/encoding

RIGHTFAX_CONTENT_TYPE

Optional.
Enter the MIME content type of
the document. Default value
“application/octetstream”.

Set to Content-Type field
of file attachment.

Note: In addition to the above listed RightFax specific properties, all delivery manager common properties, including BUFFERING_MODE (for document redelivery) and FILTER (for document conversion), are supported.


You want to set the RightFax Server in a configuration file?

I am sure you already know that BI Publisher supports a delivery configuration file to manage delivery servers, delivery properties and custom delivery channels. Now RightFax server has been added to this list with server type as 'rightfax'
<server name="myhost" type=”rightfax”>
This server type expects values for Host, port, uri, username, password, authType, encType, proxyPort, proxyUsername, proxyPassword, proxyAuthType, filter and filterOutputContentType.
Please check the BI Publisher ‘Administrator’s and Developer’s Guide’ to know more about the delivery configuration file.
Sample Delivery Configuration file (xdodelivery.cfg) -


<?xml version='1.0' encoding='UTF-8'?>
<config  xmlns="http://xmlns.oracle.com/oxp/delivery/config">
  <!--   ========================================================  -->
  <!--     servers section                                         -->
  <!--     List your pre-defined servers here.                     -->
  <!--   ========================================================  -->
  <servers>
        <server  name="myhost" type="rightfax" default="true"  >
        <host>myrightfax1.companyname.com</host>
        <port>80</port>
        <uri/>
        <username/>
        <password/>
        <authType/>
        <encType/>
        <proxyHost/>
        <proxyPort/>
        <proxyUsername/>
        <proxyAuthType/>
        <filter/>
        <filterOutputContentType/>
        </server>
        </servers>
  <!--   ========================================================  -->
  <!--     properties section                                      -->
  <!--     List the system properties here.                        -->
  <!--   ========================================================  -->
  <properties>
        <property  name="ds-temp-dir">/tmp</property>
        <property  name="ds-buffering">true</property>
  </properties>
  <!--  ========================================================  -->
  <!--      channels section                                       -->
  <!--      List the custom delivery channels  here.               -->
  <!--   ========================================================  -->
  <channels/>
</config> 

 

Can we monitor the Job Status?
When the BI Publisher Delivery Manager submits a job, it receives an XML response from RightFax server. This response includes the unique id for the fax job, which can be used to check status of the job by submitting a separate request to RightFax server. However, BI Publisher Delivery Manager is not using this unique fax job id approach. Instead, we recommend the standard approach used by all delivery channels to get the job status.
In this approach, after a job is submitted the client program can make ‘asynchronous’ call using delivery manager APIs. You can create your own callback logic by implementing DeliveryResponseListener interface. You must implement the responseReceived() method. This method should be called when the delivery request completes. You can pass this callback when you invoke the submit() method of the DeliveryRequest. This will initiate the delivery process in the background and the submit() method will immediately return the control to DeliveryResponseListener to get the delivery status. Sample code follows:

static class MyListener implements DeliveryResponseListener{
public void responseReceived(DeliveryResponse pResponse){
      System.out.println("Request  done!");
      int  status = pResponse.getStatus();
      System.out.println("Request  status id : " + status);
      String  msg = pResponse.getStatusMessage();
      System.out.println("Request  status message : " + msg);
      //  Update the status in database here...
      }
}
public static void main(String[] args){
      try
          {
                DeliveryManager  dm = new DeliveryManager();
                DeliveryRequest  req =
                dm.createRequest(DeliveryManager.TYPE_RIGHTFAX);
                req.setDocument("d:/tmp/test.ps");
                req.submit(new  MyListener());
          }
      catch  (Exception e)
          {
                e.printStackTrace();
          } 
}

In asynchronous mode, server status is checked once per minute by default but you can control this interval by setting ASYNCH_CHECK_INTERVAL delivery property before submitting the request.

Check the status codes returned by the Delivery Manager and its corresponding status codes/error codes on the RightFax server.

Delivery Manager Status/Error Code
RightFax Status/Error Code
STATUS_CLIENT_IN_PROCESS
 
STATUS_FAILED_BUSY
 
STATUS_FAILED_CLIENT_ERROR
 -1 (failure to load XML DOM – possibly XML
formatting error) -4 (XSL info missing) -5 (unknown XML operation type)
STATUS_FAILED_INVALID_RECIPIENT
 
STATUS_FAILED_INVALID_REQUEST
 
STATUS_FAILED_INVALID_USER
 
STATUS_FAILED_IO_ERROR
 
STATUS_FAILED_SERVER_ERROR
 -3 (failed to connect to RightFax Server)
 -2 (failed to load XSL into DOM)
  0 (submit/query failure)
  9 (Too many errors)
  10 (Duplicate)
  11 (Error)
  12 (Needs attention)
  13 (Needs attention)
  Any other status code
STATUS_FAILED_TIMEOUT
 
STATUS_NOT_DELIVERED
 
 

So I believe you are all set to try the document delivery to a RightFax Server.. Happy faxing !! Please note down the patch numbers -


The patch number for EBS 11i (for XML Publisher 5.6.3) is -
7665602 - RIGHT FAX CHANNEL SUPPORT WITH PROVISION FOR DELIVERY STATUS AT LATER TIME
The patch number for EBS 11i (for Collections) is -
8435600 - UNABLE TO SEE FAILURE REASON IN THE CONCURRENT PROGRAM LOG FILE

About Document Delivery

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

Data Extraction is the previous category.

General 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