Configuration and deployment of PDF printing in APEX 4.1.1 using GlassFish 3.1.2 and Apache FOP

Environment: Oracle database 11.2.0.3, APEX 4.1.1, GlassFish 3.1.2 Open Source Edition (Web Profile), Oracle Linux 6.2

Apache FOP (XSL-FO Processor) can be used together with APEX to allow PDF printing of classic or interactive reports. In this blog post, I will explain how you can deploy and configure FOP in APEX 4.1.1 with GlassFish 3.1.2.

Apache FOP can be installed on a Java application server by deploying the fop.war web archive, which can be found in the /utilities/fop folder inside the APEX installation files. However, the fop.war file does not work out of the box on GlassFish 3.1.2.

I faced two different kind of errors in my installation:

1) package oracle.xml.parser.v2 does not exist: this is because GlassFish can’t find the xmlparserv2.jar file which can be found in $ORACLE_HOME/lib.

2) java.lang.IllegalStateException: getOutputStream() has already been called: this has something to do with getOutputStream() being used more than once; I’m far from a Java specialist, but I found a workaround for this problem on the internet.

This is the solution:

First, we need to extract the original fop.war file (I used the jar utility from my Java 6 JDK installation):

$ /u01/app/java/java6/bin/jar -xf fop.war

You should see the following:

$ ls -la

-rw-r–r– 1 oracle oinstall 1792 May 4 12:44 apex_fop.jsp
-rw-r–r– 1 oracle oinstall 7016135 May 4 12:44 fop.war
drwxr-xr-x 4 oracle oinstall 4096 May 4 14:00 WEB-INF

Next, copy the xmlparserv2.jar file from $ORACLE_HOME/lib into the WEB-INF/lib folder:

$ cp -p /u01/app/oracle/product/11.2.0/db_1/lib/xmlparserv2.jar WEB-INF/lib

Now we need to modify the two apex_fop.jsp files in the top folder and under WEB-INF/classes. Add the following 2 lines just before the final “driver.run();” call:

out.clear();
out = pageContext.pushBody();

The full apex_fop.jsp file should look like this:

<%@ page import='java.io.*'%>
<%@ page import='org.xml.sax.InputSource'%>
<%@ page import='org.apache.fop.apps.Driver'%>
<%@ page import='org.apache.fop.apps.Options'%>
<%@ page import='oracle.xml.parser.v2.XMLDocument'%>
<%@ page import='oracle.xml.parser.v2.XSLProcessor'%>
<%@ page import='oracle.xml.parser.v2.XSLStylesheet'%>
<%@ page import='oracle.xml.parser.v2.DOMParser'%>
<%
response.setContentType("application/pdf");

XMLDocument   v_doc;
XSLStylesheet v_xsl = null;
String        v_fop;
DOMParser     parser = new DOMParser();
XSLProcessor processor = new XSLProcessor();
// set the encoding for the XML Processing
String        v_encode = "UTF-8";

// get the XSL
v_xsl = new XSLStylesheet(new java.io.StringReader(request.getParameter("template")),null);

// get the XML String from the form which was posted
parser.parse(new java.io.StringReader(request.getParameter("xml")));
// get the XML Document
v_doc = parser.getDocument();

// create an output stream to get the transformed results
ByteArrayOutputStream v_out = new ByteArrayOutputStream();
// transform the xml and xsl to get an FOP
processor.processXSL(v_xsl, v_doc, v_out);
// convert the FOP byte array to a string with encoding set above
v_fop = new String(v_out.toByteArray(),v_encode);

//
// Now call the apache FOP processing
//
Driver driver = new Driver();
// set the desired output
// see http://xml.apache.org/fop/output.html for all output types
driver.setRenderer(Driver.RENDER_PDF);

// set the input for the FOP engine
driver.setInputSource(new InputSource(new StringReader(v_fop)));
// set the output to stream to the browser
driver.setOutputStream(response.getOutputStream());
out.clear();
out = pageContext.pushBody();
// process
driver.run();
%>

Now we are going to recreate the fop.war file:

$ /u01/app/java/java6/bin/jar -cvf fop.war apex_fop.jsp WEB-INF

We are now ready to deploy the fop.war file in GlassFish:

- start up the GlassFish admin console (default: port 4848)

- click on Standalone Instances -> your instance

- click on Applications -> Deploy -> local packaged file… and browse to the “fop.war” file (see screenshot below)

- click on OK; “fop” should now appear in the list of Deployed Applications

After this, you can already test your Apache FOP deployment by opening http://<servername&gt;:<port>/fop/apex_fop.jsp in a web browser. If you see a Java NullPointerException error, things are looking good ;-)

Finally, we need to tell APEX where to find the print server:

- log in to the APEX workspace “internal” with the username “admin”

- click on Manage Instance -> Instance Settings

- fill in the Report Printing part (see screenshot below):

Print Server: External (Apache FOP)
Print Server Host Address: localhost
Print Server Port: (your APEX port)
Print Server Script: /fop/apex_fop.jsp

That’s it! Now try to print a report as PDF in APEX; it should work fine!

 

Matthias

 

 

About these ads

21 Responses to Configuration and deployment of PDF printing in APEX 4.1.1 using GlassFish 3.1.2 and Apache FOP

  1. Admin says:

    Reblogged this on APEX World and commented:
    Great POST!!!!

  2. Claudio says:

    This setup works with glassfish 3.1.1.?

    I appreciate your attention!

    • matthiashoys says:

      Yes, I just tried it on GlassFish 3.1.1 and it’s also working!

      Good luck :-)
      Matthias

      • Claudio says:

        Hi,
        All settings were performed as directed in the post, but did not work is giving the following error when generating printing.

        Error: ORA-20001: The printing engine could not be reached because either the
        URL specified is incorrect or a proxy URL needs to be specified.

        is_internal_error: true
        apex_error_code: APEX.UNHANDLED_ERROR
        ora_sqlcode: -20001

        ora_sqlerrm: ORA-20001: ORA-20001: The printing engine could not be reached because either the
        URL specified is incorrect or a proxy URL needs to be specified.

        error_backtrace:

        ORA-06512: em “APEX_040100.WWV_FLOW_PRINT_UTIL”, line 117
        ORA-06512: em “APEX_040100.WWV_FLOW_RENDER_QUERY”, line 1619
        ORA-06512: em “APEX_040100.WWV_FLOW_RENDER_QUERY”, line 1681
        ORA-06512: em “APEX_040100.WWV_FLOW”, line 7261

        I can not find the solution.
        Can you help me?
        Thanks!

      • matthiashoys says:

        Hello Claudio,

        You probably need to give your APEX_040100 user network access, as explained in the APEX 4.1 installation guide. Access to network services is by default disabled in Oracle 11g.

        You can do this by executing the following PL/SQL code with user SYS as SYSDBA:

        DECLARE
        ACL_PATH VARCHAR2(4000);
        ACL_ID RAW(16);
        BEGIN
        -- Look for the ACL currently assigned to '*' and give APEX_040100
        -- the "connect" privilege if APEX_040100 does not have the privilege yet.

        SELECT ACL INTO ACL_PATH FROM DBA_NETWORK_ACLS
        WHERE HOST = '*' AND LOWER_PORT IS NULL AND UPPER_PORT IS NULL;

        -- Before checking the privilege, ensure that the ACL is valid
        -- (for example, does not contain stale references to dropped users).
        -- If it does, the following exception will be raised:
        --
        -- ORA-44416: Invalid ACL: Unresolved principal 'APEX_040100'
        -- ORA-06512: at "XDB.DBMS_XDBZ", line ...
        --
        SELECT SYS_OP_R2O(extractValue(P.RES, '/Resource/XMLRef')) INTO ACL_ID
        FROM XDB.XDB$ACL A, PATH_VIEW P
        WHERE extractValue(P.RES, '/Resource/XMLRef') = REF(A) AND
        EQUALS_PATH(P.RES, ACL_PATH) = 1;

        DBMS_XDBZ.ValidateACL(ACL_ID);
        IF DBMS_NETWORK_ACL_ADMIN.CHECK_PRIVILEGE(ACL_PATH, 'APEX_040100',
        'connect') IS NULL THEN
        DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(ACL_PATH,
        'APEX_040100', TRUE, 'connect');
        END IF;

        EXCEPTION
        -- When no ACL has been assigned to '*'.
        WHEN NO_DATA_FOUND THEN
        DBMS_NETWORK_ACL_ADMIN.CREATE_ACL('power_users.xml',
        'ACL that lets power users to connect to everywhere',
        'APEX_040100', TRUE, 'connect');
        DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL('power_users.xml','*');
        END;
        /
        COMMIT;

        Let me know if this helps,
        Matthias

  3. Claudio says:

    I remember that I am using the apex listener.

  4. apex_newbee says:

    thanks a lot it was very helpfull

  5. mike says:

    Would you be able to provide additional information about the error: Error: ORA-20001: The printing engine could not be reached because either the
    URL specified is incorrect or a proxy URL needs to be specified.? I have verified that I have network access for my user apex_040100. I can get as far as the java null exception, but when I attempt to print a report it states the error above.

    Thanks!

    • matthiashoys says:

      That’s strange… Granting network access privileges should get rid of that error. I would think that there is something wrong with your ACL’s…

      What do the following queries return? What version of Oracle database do you use?

      select acl , host , lower_port , upper_port from DBA_NETWORK_ACLS;
      select acl , principal , privilege , is_grant from DBA_NETWORK_ACL_PRIVILEGES;

      Matthias

      • mike says:

        I ran into the same problem and found that the printer port that was set for the APEX instance was wrong. I set it to 8080 and it started working.

        As SYS user:

        SQL> ALTER SESSION SET CURRENT_SCHEMA = APEX_040100;

        To check current printer port setting:

        SQL> SELECT APEX_INSTANCE_ADMIN.GET_PARAMETER(‘PRINT_SVR_PORT’) port from dual;

        PORT
        ——-
        7777

        To change port setting:

        SQL> BEGIN
        APEX_INSTANCE_ADMIN.SET_PARAMETER(‘PRINT_SVR_PORT’, 8080);
        END;
        /

        SQL> commit;

        I also had a problem with “^M” end-of-line characters in the fop.war file. I just did a “dos2unix” command on the files and that fixed the run-time Java error I was getting.

        Other than these two problems that were my fault these instructions worked perfectly. Thanks.

  6. Mitt says:

    Does this work with custom Report Layouts?

    I tried your setup and printed a report query with a generic layout which worked just fine. With a custom layout though, I get the following error.

    org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
    at com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.checkDOMNSErr(CoreDocumentImpl.java:2526)

  7. Jose says:

    Hello Matthias, i follow this instruccion and when create a pdf file from apex, this file has 1kb and its imposible to open whith acrobat reader.
    Can you help me ?
    Thanks

    • Jose says:

      I open de pdf file whith notepad and the error message is “error de informe:
      ORA-20001: No se ha podido acceder al motor de impresión debido a que la dirección URL especificada es incorrecta o a que se debe especificar una dirección URL de proxy.”
      My configuration is
      Print Server : Apache FOP
      Protocol = HTTP
      Direccion = localhost or http://localhost i try with both
      Port: 8080
      Script = /fop/apex_fop.jsp

    • matthiashoys says:

      Hmm, then there must be something wrong with the deployment. What happens when you open the page apex_fop.jsp with a web browser?

  8. Todd Robinson says:

    Beautiful post. Your solution worked perfectly in my APEX 4.2.6 installation using Glassfish.
    Thanks so much.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 44 other followers

%d bloggers like this: