Raible's Wiki
Raible Designs AppFuseHomepage- Korean - Chinese - Italian - Japanese QuickStart Guide User Guide Tutorials Other ApplicationsStruts ResumeSecurity Example Struts Menu
Set your name in
UserPreferences
Referenced by
JSPWiki v2.2.33
Hide Menu |
This is version 13.
It is not the current version, and thus it cannot be edited. A HowTo for generating PDF documents using Apache FOP in the AppFuse architecture. About this TutorialThis tutorial will show you how to create a Servlet that can generate a PDF document with data retrieved from our UserDAO object, and a JUnit Test (using StrutsTestCase) that will demonstrate the different stages of the PDF generation process.The FOP process I am going to describe is producing XML from a POJO entity, passing that XML through an XSL stylesheet to produce a FO XML document, then passing the FO document through an XSLT processor to produce the PDF document. Please note, the resulting PDF document does not look very pretty. This is because the XSL stylesheet is very basic and does not really apply much FO formatting tags/attributes. It would be very easy to modify the XSL to include images, bold, grey background cells etc.. but this is best left for the FOP documentation to describe. Table of Contents
Create an InputSource for your POJO [#1]Now let's write our UserInputSource object. This class is used to wrap a SAXSource around our User object, which is then used for generating the XML document in Stage [2].UserInputSource needs to extend interface FOPSource which should be in src/service/**/fop/FOPSource.java (source attached at the end of this tutorial). In src/service/**/fop, create a UserInputSource.java file with the following contents:
Create XMLReader for your User POJO [#2]Now we need to create the XMLReader which will generate SAX events from our User object. This object should write any data from the User object, we might want to publish in our PDF document. This class depends on two helper classes, AbstractObjectReader and EasyGenerationContentHandlerProxy that need to be added to src/service/**/fop. These files are downloadable at the end of this tutorial.
Create XSL stylesheet to produce FO [#3]The xsl stylesheet will process the XML generated by the UserXMLReader and produce a FO document. The FO document is processed by Apache FOP to produce the PDF document. Therefore, the XSL stylesheet is where we can modify the FO formatting and style the PDF document. Place the user2fo.xsl stylesheet in src/web/**/fop/user2fo.xsl. <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" exclude-result-prefixes="fo"> <xsl:output method="xml" version="1.0" omit-xml-declaration="no" indent="yes"/> <!-- ================== --> <!-- root element: user --> <!-- ================== --> <xsl:template name="user" match="user"> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="simpleA4" page-height="29.7cm" page-width="21cm" margin-top="2cm" margin-bottom="2cm" margin-left="2cm" margin-right="2cm"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="simpleA4"> <!-- START MAIN BODY --> <fo:flow flow-name="xsl-region-body"> <!-- Title Text --> <fo:block font-weight="bold" text-align="start" space-after="12pt"> User Report </fo:block> <!-- START MAIN TABLE --> <fo:block> <fo:table table-layout="fixed" border="solid black 1px"> <fo:table-column column-width="4.3cm" column-number="1"/> <fo:table-column column-width="4.3cm" column-number="2"/> <fo:table-body> <fo:table-row> <fo:table-cell font-weight="bold" padding="2px" border="solid black 1px"> <fo:block> First name </fo:block> </fo:table-cell> <fo:table-cell padding="2px" border="solid black 1px"> <fo:block> <xsl:value-of select="firstName"/> </fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell font-weight="bold" padding="2px" border="solid black 1px"> <fo:block> Last name </fo:block> </fo:table-cell> <fo:table-cell padding="2px" border="solid black 1px"> <fo:block> <xsl:value-of select="lastName"/> </fo:block> </fo:table-cell> </fo:table-row> <!-- When the apply-templates is executed by the XSLT processor all matching child nodes (from the XML document are processed). So, in our example, any role elements nested within a User will be processed by the xsl:template below that matches on role --> <xsl:apply-templates/> </fo:table-body> </fo:table> </fo:block> <!-- END MAIN TABLE --> </fo:flow> <!-- END MAIN BODY --> </fo:page-sequence> </fo:root> </xsl:template> <!-- =================== --> <!-- child element: role --> <!-- =================== --> <!-- If there are multiple role elements nested within the User then a tale-row per role end up in the PDF doc --> <xsl:template match="role"> <fo:table-row> <fo:table-cell font-weight="bold" padding="2px" border="solid black 1px"> <fo:block> Role name </fo:block> </fo:table-cell> <fo:table-cell padding="2px" border="solid black 1px"> <fo:block> <xsl:value-of select="roleName"/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:template> </xsl:stylesheet>
Modify User Junit test [#4]To support the execution of the User service level test, we need another helper file. This file (attached at the end) is called FOPHelper.java and lives in src/service/**/utils. Add the following imports and method to UserManagerTest.java:
Run User Junit test [#5]Now execute ant test-service. The testGeneratePDF method will execute and generate the 3 files involved in the PDF generation process. user.xml, user.fo and user.pdf (all three attached at the end of this tutorial). You should see output: [junit] [appfuse] INFO [main] UserManagerTest.testGeneratePDF(83) | xslFile exists [junit] [appfuse] DEBUG [main] UserManagerTest.testGeneratePDF(88) | Input: a User object [junit] [appfuse] DEBUG [main] UserManagerTest.testGeneratePDF(89) | Stylesheet: ././src/web/org/appfuse/webapp/fop/user2fo.xsl [junit] [appfuse] DEBUG [main] UserManagerTest.testGeneratePDF(90) | Output: PDF (./user.pdf) [junit] [appfuse] DEBUG [main] UserManagerTest.testGeneratePDF(91) | Transforming... Create a servlet to handle the requests [#6]This servlet will take a userName from the HttpRequest object, call the UserDAO to load a User. It then calls UserXmlReader to generate the XML, does the XSLT transformation, and generates the PDF document. During this process, no files are saved to disk. Add this servlet so src/web/**/action/ {Java2HtmlPlugin package org.appfuse.webapp.action; import org.appfuse.model.User; import org.appfuse.dao.UserDAO; import org.appfuse.fop.UserInputSource; import org.appfuse.fop.UserXMLReader; import org.appfuse.fop.FOPHelper; import org.apache.avalon.framework.logger.ConsoleLogger; import org.apache.avalon.framework.logger.Logger; import org.apache.fop.apps.Driver; import org.apache.fop.apps.XSLTInputHandler; import org.apache.fop.apps.TraxInputHandler; import org.apache.fop.messaging.MessageHandler; import java.io.*; import java.util.Date; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.context.support.ServletContextResource; import org.springframework.core.io.*; import org.xml.sax.InputSource; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; /** * Implementation of HttpServlet that is used * to autogenerate a PDF document. * * * * @author Ben Gill * @version $Revision: 1.1 $ $Date: 2005/03/12 17:46:09 $ * * @web.servlet * display-name="FOP Servlet" * load-on-startup="1" * name="fop" * * @web.servlet-mapping * url-pattern="/fop/*" */ public class FOPServlet extends HttpServlet { private Logger logger = null; private ApplicationContext ctx = null; private ServletContext servletContext = null; public void init() throws ServletException { this.servletContext = getServletContext(); this.ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); } /** * Route the user to the execute method * * @param request The HTTP request we are processing * @param response The HTTP response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { execute(request, response); } /** * Route the user to the execute method * * @param request The HTTP request we are processing * @param response The HTTP response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { execute(request, response); } /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). * * @param request The HTTP request we are processing * @param response The HTTP response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public void execute(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ByteArrayOutputStream out = null; try { String userName = request.getParameter("userName"); UserDAO dao = (UserDAO)ctx.getBean("userDAO"); User user = dao.getUser(userName); UserInputSource uis = new UserInputSource(user); ClassPathResource resource = new ClassPathResource("/org/appfuse/webapp/fop/user2fo.xsl"); byte[] content = FOPHelper.createPDF(uis, resource); response.setHeader("Content-Disposition", "attachment; filename=user-" + user.getUsername() + ".pdf"); response.setContentType("application/pdf"); response.setContentLength(content.length); response.getOutputStream().write(content); response.getOutputStream().flush(); } catch (Exception e) { throw new ServletException(e); } finally { if (out!=null) { out.close(); } } } } ] !!Modify JSP to call the servlet [#7] To link to the servlet from the main app. Add the following column to the table in src/web/pages/userList.jsp: {{{Attachments:
|