Sunday, February 14, 2016

APEX 5.0 and Apache FOP.

Couple days ago I've run into curious issue, how to print Classic report from Apex 5 application.
I have no ORDS 3. on my humble Fedora VM, and I not realy want to install it. Oracle BI Publisher ain't option  (unless you run corporate reports on OBIEE 11g/12c).  Well Apex has the 3rd option - External - with  Apache FOP.

Yeah, right! It has to be a servlet in your apex/utilities/fop folder. OOps, it used to be there.
Well, not a big deal, anyway it is a single JSP application with Apache FOP libraries and one big issue - it works out of the box only on Oracle Application servers, for all other cases you should get somewhere Oracle XML Parser libraries (for example - download XDK). processors. Out of this great discussion you may find whole source of fop.war servlet.
Okay then, we will create our own servlet with blackjack & ..., oops with FOP and standard Java XML parser.
what APEX expects from our application. It sends two parameters:

  1. xml   - document with report data
  2. template -  XSLT stylesheet to process data
Application should respond back with PDF document.


 To accomplish this simple task you  need:
  • Apache FOP runtime libraries.
  • Some application server to run our JSP application. (Let's stick with Apache and get a Tomcat)
  • JSP to get parameters from APEX and generate PDF page.

Let's chase to the source.

<%@ page import='org.w3c.dom.Document' %>
<%@ page import='java.io.*' %>
<%@ page import='org.xml.sax.InputSource' %>
<%@ page import='javax.xml.transform.stream.StreamSource' %>
<%@ page import='javax.xml.transform.stream.StreamResult' %>
<%@ page import='javax.xml.parsers.DocumentBuilderFactory' %>
<%@ page import='javax.xml.parsers.DocumentBuilder' %>
<%@ page import='javax.xml.transform.TransformerFactory' %>
<%@ page import='javax.xml.transform.Transformer' %>
<%@ page import='javax.xml.transform.dom.DOMSource' %>
<%@ page import='org.apache.fop.apps.Driver' %>

<%
//Set response type to PDF
response.setContentType("application/pdf");
// Prepare XML document 
    DocumentBuilder db = DocumentBuilderFactory.
                              newInstance().newDocumentBuilder();
// Parse parameter xml
    Document v_xml = db.parse(new InputSource(
                                new java.io.StringReader(
                                        request.getParameter("xml")
                      )));

// Create XSLT processor with instructions from template parameter
    Transformer v_xsl  = TransformerFactory.newInstance().newTransformer(
                                new StreamSource( 
                                        new java.io.StringReader(
                                                request.getParameter("template"))
                         ));
// Prepare output Stream
  ByteArrayOutputStream v_out = new ByteArrayOutputStream();
// Transform data into XSL-FO document 
  v_xsl.transform(new DOMSource(v_xml),new StreamResult(v_out));
  String v_fop = new String(v_out.toByteArray(),"UTF-8");
// Initialize FO driver
  Driver drv = new Driver();
  drv.setRenderer(Driver.RENDER_PDF);
// Prepare  output for PDF document 
   v_out = new ByteArrayOutputStream();
  drv.setOutputStream(v_out);
// Set Input source for FO driver
  drv.setInputSource(new InputSource(new StringReader(v_fop)));
// Produce document
  drv.run();

// Returnresult for client  
// Fix to avoid Tomcat/JBoss double open exception
  OutputStream outStream = response.getOutputStream();
  response.setContentLength(v_out.size());
  outStream.write(v_out.toByteArray());
  outStream.flush();
  
%>



Now make sure, that your WEB-INF/lib folder contains libraries to run FOP driver.  I've found old distribution with few libraries. that is how it looks like in my NetBeans Libraries list.

Well, that's all folks. If you use NetBeans or just have got my war archive - time to have fun.
Copy archive to your Tomcat webapps folder and try to open /fop page. 
You should see this some ugly page as on picture below. This means your application is up and running and click on "test servlet" URL will show you generated PDF document.

I don't see how you may run into issues with that, on my Tomcat 7/JDK 1.7 it runs out of the box.
Now you are ready to change Apex print configuration.
Set Report printing properties as below.
Print Server:External (Apache FOP)
Print Server Protocol:HTTP
Print Server Host Address:FQDN of your Tomcat server
Print Server Port:Your Tomcat listen port (8080 by default)
Print Server Script:/fop/pdf_print.jsp
It's time to run "real-life" example. I have a tiny application with standard report page and to print reports you should enable printing. It's quite easy for primitive examples like mine. Go to report region and open Print Attributes tab and dig into Printing  section (I left report customization for you, you've seen my index page design, right ).  What I used to set for report:
Enable Report PrintingYes
Link LabelGet PDF
I't just a name, put anything you like
Response HeaderReport Settings
View File AsInline
Personally I prefer to open PDF in browser, you may pick attachment and download report as PDF file.
Output formatPDF
NO reason to select anything else. It is a PDF generator isn't it?
If you know meaning of the other parameters you can change them. I left rest of the items intact. Save region settings and run your report page. You will see your Link label next to Download and if you click on it - you will get your PDF printer like I've got mine.


Here is web application to save your time and there is a GIT source code and libraries.

Enjoy and take care. 

1 comment:

rahul said...

thanks this war file is good.
could you please share how to create this war file.