Configuration and deployment of PDF printing in APEX 4.1.1 using GlassFish 3.1.2 and Apache FOP
May 4, 2012 13 Comments
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>:<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


Reblogged this on APEX World and commented:
Great POST!!!!
Thanks :-)
You’re welcome!! It’s a great POST and should be shared
This setup works with glassfish 3.1.1.?
I appreciate your attention!
Yes, I just tried it on GlassFish 3.1.1 and it’s also working!
Good luck :-)
Matthias
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!
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:
DECLAREACL_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
I remember that I am using the apex listener.
Hi,
Worked
Thanks!
thanks a lot it was very helpfull
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!
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
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.