Products
Technologies

Developer resources

LCDS: Using the PDFResourceServlet to read the generated PDF

Avg. Rating 4.3

Problem

You use LCDS to generate a PDF on the fly. But the documentation is not complete about how to open the document that has been written to the session.

Solution

Register the PDFResourceServlet in the web.xml file of your web application and call it, passing the same ID that was used to write the PDF in the session.

Detailed explanation

Edit \flex.war\WEB-INF\flex\remoting-config.xml in order to register the PDFService remote object that will generate the PDF document on the fly:


    <destination id="PDFService">
        <properties>
            <!-- source is the Java class name of the destination -->
            <source>com.mycompany.flex.remoteObjects.PDFService</source>
        </properties>
    </destination>


   
In Flex, write a command (in this example, using Cairngorm) to call the PDF service and open the PDF when the URL is received from the server.


package com.mycompany.core.commands
{
    import com.adobe.cairngorm.business.Responder;
    import com.adobe.cairngorm.commands.ICommand;
    import com.adobe.cairngorm.control.CairngormEvent;
    import com.mycompany.commands.common.CommonFaultHandler;
    import com.mycompany.core.business.GenerateAndOpenPDFDelegate;
    import com.mycompany.core.control.event.GenerateAndOpenPDFEvent;
    import com.mycompany.core.model.GISModelLocator;
    import com.mycompany.event.view.ProgressBarEvent;
   
    import flash.net.URLRequest;
    import flash.net.navigateToURL;
   
    import mx.collections.*;
    import mx.rpc.events.FaultEvent;
   
   
    public class GenerateAndOpenPDFCommand implements ICommand, Responder
    {
        [Bindable]
        private var model:GISModelLocator = GISModelLocator.getInstance();
       
        public function execute(event:CairngormEvent):void
        {
            trace("Executing GenerateAndOpenPDFCommand...");

            var delegate : GenerateAndOpenPDFDelegate = new GenerateAndOpenPDFDelegate( this );  
            var generateAndOpenPDFEvent : GenerateAndOpenPDFEvent = event as GenerateAndOpenPDFEvent;
            delegate.generateAndOpenPDF( generateAndOpenPDFEvent.pdfVO );         
        }
       
        public function onResult( event : * = null ) : void
        {
            var url:String = event.result as String;
            navigateToURL(new URLRequest(url), "_blank");

            // Hide the progress bar overlay
            this.model.messageTarget.dispatchEvent(new ProgressBarEvent(ProgressBarEvent.PROGRESS_BAR_EVENT, false, false, false));
        }

        public function onFault( event : * = null ) : void {
            CommonFaultHandler.getInstance().handleFault(event as FaultEvent);            
        }
    }
}

--------------------------------------------------------------------------------------------------------

Write the delegate that actually calls the service that was registered in remoting-config.xml:

package com.mycompany.core.business
{
    import mx.rpc.AsyncToken;
    import mx.rpc.events.ResultEvent;
    import mx.core.Application;

    import com.mycompany.core.business.Services;
    import com.mycompany.core.model.GISModelLocator;   
    import com.mycompany.core.vo.PDFVO;
    import com.mycompany.event.view.ProgressBarEvent;

    import com.adobe.cairngorm.business.Responder;
    import com.adobe.cairngorm.business.ServiceLocator;

   
    public class GenerateAndOpenPDFDelegate
    {
       private var responder : Responder;
       private var service : Object;

       [Bindable]
       private var model:GISModelLocator = GISModelLocator.getInstance();


       public function GenerateAndOpenPDFDelegate( responder : Responder )
       {     
          this.service = ServiceLocator.getInstance().getRemoteObject("pdfService");
          this.responder = responder;     

            // Show the progress bar overlay
           
            this.model.messageTarget.dispatchEvent(new ProgressBarEvent(
                ProgressBarEvent.PROGRESS_BAR_EVENT, false, false, true, this.service, this.model.languageDictionary["000673"]));
        }
      
       //---------------------------------------------------------------
   
       public function generateAndOpenPDF(pdfVO:PDFVO) : void
       {  
            var document:XML = pdfVO.document;
            var filename:String = pdfVO.filename;

            if(document && filename)
            {          
                var token : AsyncToken = service.generatePDF(document, filename);
                token.resultHandler = responder.onResult;
                token.faultHandler = responder.onFault;
            }
        }
    }
}



Create the Java PDFService in Eclipse. This is the remote object that is registered in remoting-config.xml. In order for this to compile, you will need to add some of the JAR files provided by Adobe in \flex.war\WEB-INF\lib in your Eclipse build path (acrobat-core.jar, acrobat-core-charset.jar, flex-acrobat.jar, pdfencryption.jar). This remote object uses the XFAHelper in order to generate the PDF on the fly and writes it to a byte array in the user's session. The dataset argument to the generatePDF method is the XML data sent by the Flex UI, while the PDFdocument argument is the filename of the PDF form that was created using Adobe Livecycle Designer, which contains all the data bindings for the Text Fields (caption and value) as well as Image Fields.


package com.mycompany.flex.remoteObjects;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.w3c.dom.Document;

import flex.acrobat.pdf.XFAHelper;
import flex.messaging.FlexContext;
import flex.messaging.FlexSession;
import flex.messaging.util.UUIDUtils;

public class PDFService
{
    public PDFService()
    {
    }

    public Object generatePDF(Document dataset, String PDFdocument) throws IOException
    {
        // Open shell PDF
        String source =    FlexContext.getServletContext().getRealPath("/pdfgen/" + PDFdocument);
        int index = source.indexOf("./");

        if(index != -1 )
        {
            // Remove the ./ added by UNIX
            source = source.substring(0, index) + source.substring(index + 2, source.length());
        }

        XFAHelper helper = new XFAHelper();
        helper.open(source);

        // Import XFA dataset
        helper.importDataset(dataset);
       
        // Save new PDF as a byte array in the current session
       
        byte[] bytes = helper.saveToByteArray();
        String uuid = UUIDUtils.createUUID(false);
        FlexSession session = FlexContext.getFlexSession();
        session.setAttribute(uuid, bytes);
       
        // Close any resources
        helper.close();
       
        HttpServletRequest req = FlexContext.getHttpRequest();
        String contextRoot = FlexContext.getServletContext().getContextPath();
       
        if (req != null)
            contextRoot = req.getContextPath();

        String r = contextRoot + "/dynamic-pdf?id=" + uuid + "&;jsessionid=" + session.getId();
        System.out.println(r);
       
        return r;
    }
}


Note the /dynamic-pdf path and the id returned to Flex.

Create the Java PDFResourceServlet in Eclipse. This servlet uses the ID in order to retrieve the PDF byte array and send it back to the client, setting the content type to "application/pdf":


package com.mycompany.flex.servlets;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class PDFResourceServlet extends HttpServlet
{

 public PDFResourceServlet()
 {
 }

 protected void doGet(HttpServletRequest req, HttpServletResponse res)
     throws ServletException, IOException
 {
     doPost(req, res);
 }

 protected void doPost(HttpServletRequest req, HttpServletResponse res)
     throws ServletException, IOException
 {
     String id = req.getParameter("id");
     if(id != null)
     {
         HttpSession session = req.getSession(true);
         try
         {
             byte bytes[] = (byte[])(byte[])session.getAttribute(id);
             if(bytes != null)
             {
                 res.setContentType("application/pdf");
                 res.setContentLength(bytes.length);
                 res.getOutputStream().write(bytes);
             } else
             {
                 res.setStatus(404);
             }
         }
         catch(Throwable t)
         {
             System.err.println(t.getMessage());
         }
     }
 }

 private static final long serialVersionUID = 0x7180e4383e53d335L;
}


In web.xml under \deploy\flex.war\WEB-INF, add the following nodes:

   
    <!-- PDF Resource Servlet -->
   
    <servlet>
        <servlet-name>PDFResourceServlet</servlet-name>
        <display-name>PDFResourceServlet</display-name>
        <servlet-class>com.mycompany.flex.servlets.PDFResourceServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>PDFResourceServlet</servlet-name>
        <url-pattern>/dynamic-pdf/*</url-pattern>
    </servlet-mapping>

Notice how the <url-pattern> node value matches the URL returned to Flex by the PDF remote object.

 

Report abuse

Related recipes