1

I'm using Apache Fop with XSL-FO to generate a PDF. I'm then trying to stream the pdf as an attachment to apache.commons.mail.HtmlEmail; I receive emails with the attachment, however I get the following error. Length 0 bytes, encoding none. I'm able to create a pdf on the file system without any issue, so I know there is nothing wrong with the FOP portion of this code, so I'm not really sure why it's not working. Could someone tell me what I'm missing?

My code.

private void sendBroadcastNofications(PurchaseRequest pr) {
    try {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        //Fop sevice used to generate pdf.
        this.xmlPDFGeneratorService.generatePDF(pr, outputStream);
        byte[] bytes = outputStream.toByteArray();

        //I'm not sure if "application/pdf" would be an issue since fop is giving 
        //it the MimeConstants.MIME_PDF
        DataSource source = new ByteArrayDataSource(bytes, "application/pdf");            
        EmailAttachment attachment = new EmailAttachment(source, "purchase_requisition.pdf", "Broadcast Purcahse Requisition");

        Util.email("Purchase Request " + pr.getPrNumber(), getGenerateMessage(pr), pr.getAuthorizer().getEmail(), attachment);

    } catch (IOException ex) {
        Logger.getLogger(EmailServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
    }
}


public static void email(String subject, String message, String recipient, EmailAttachment attachment) {
    email(subject, message, Collections.singleton(recipient), Collections.singleton(attachment));
}

public static void email(String subject, String message, Set<String> recipients, Set<EmailAttachment> attachments) {
    try {
        HtmlEmail email = new HtmlEmail();

        email.setHostName("mailhost");
        email.setSubject(subject);
        email.setHtmlMsg(message);
        email.setFrom("company@domain.com");

        for (EmailAttachment attachment : attachments) {
            try {
                email.attach(attachment.getDataSource(), attachment.getName(), attachment.getDescription());
            } catch (EmailException ex) {
                System.err.println("Email Attachment Exception: " + ex.getMessage());
            }
        }

        for (String recipient : recipients) {
            email.addTo(recipient);
        }
        email.send();
    } catch (EmailException ex) {
        System.err.println("Email Failed to Send: " + ex.getMessage());
    }
}

Fop Class

public class XMLPDFGeneratorServiceImpl implements XMLPDFGeneratorService {

private static final File baseDir = new File(".");
private static final File xsltfile = new File(baseDir, "./PurchaseRequestPDF.xsl");

// configure fopFactory as desired
private final FopFactory fopFactory = FopFactory.newInstance();

public void generatePDF(PurchaseRequest pr, ByteArrayOutputStream outStream) {

    // configure foUserAgent as desired
    FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

    try {

        // Setup output
        outStream = new ByteArrayOutputStream();

        // Construct fop with desired output format
        Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, outStream);

        // Setup XSLT
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(new StreamSource(xsltfile));

        // Set the value of a <param> in the stylesheet
        transformer.setParameter("versionParam", "2.0");

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        generateXML(pr, stream);  

        byte[] out = stream.toByteArray(); 
        stream.close();

        ByteArrayInputStream in = new ByteArrayInputStream(out);
        // Setup input for XSLT transformation
        Source src = new StreamSource(in);

        // Resulting SAX events (the generated FO) must be piped through to FOP
        Result res = new SAXResult(fop.getDefaultHandler());

        // Start XSLT transformation and FOP processing
        transformer.transform(src, res);

    } catch (TransformerException ex) {
        Logger.getLogger(XMLPDFGeneratorServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
        Logger.getLogger(XMLPDFGeneratorServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
    } catch (FOPException ex) {
        Logger.getLogger(XMLPDFGeneratorServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
    }
}

public void generateXML(PurchaseRequest pr, ByteArrayOutputStream stream) {
    try {
        // create JAXB context and instantiate marshaller
        JAXBContext context = JAXBContext.newInstance(PurchaseRequest.class);
        Marshaller m = context.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        m.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
        m.marshal(pr, new PrintWriter(stream));
    } catch (JAXBException ex) {
        Logger.getLogger(XMLPDFGeneratorServiceImpl.class.getName()).log(Level.SEVERE, "JAXB Failed to produce xml stream", ex);
    }
}

}

Code Junkie
  • 7,602
  • 26
  • 79
  • 141
  • Pretty sure `application/pdf` is *not* your issue. I have used the same when transferring PDF files myself. – BlackVegetable Aug 17 '12 at 16:38
  • #BlackVegetable I added my fop class, perhaps maybe you'll be able to spot something I may be missing. – Code Junkie Aug 17 '12 at 17:24
  • I've actually been working with SugarCRM in this instance. Because of the way their API is set up, it requires me to save the file to disk (locally) and then refer to that file object for the transfer. I know this isn't a direct answer to your question, but you may want to consider saving your file locally, attempting to send it, and if your sending succeeds, delete the file locally. I cannot find anything wrong with your FOP code at a glance. Because you are able to create a document on your file system, I doubt your issue is there either. – BlackVegetable Aug 17 '12 at 17:36
  • yee that doesn't sound ideal. – Code Junkie Aug 17 '12 at 18:03

1 Answers1

3

you are overwriting the outStream in generatePDF(), therefore the one that you pass in stays empty. Remove that line and try again.

outStream = new ByteArrayOutputStream();
centic
  • 15,565
  • 9
  • 68
  • 125