The stacktraces were added to allow me to quickly debug without trapesing
through the mega log that log4J is spewing out from the whole system (I know
its wrong hell I cringe whenever I see some of the code but it is based on
the old system just upgraded for Fop 1.0 as the old system was 0.23 with a
handful of custom addons like accepting font-weight="bold" and z-index="2"
to using a bare 1.0

 

 

Kindest regards

 


Theresa Forster

Senior Software Developer



From: Craig Ringer [mailto:ring...@ringerc.id.au] 
Sent: 03 February 2012 14:00
To: fop-users@xmlgraphics.apache.org
Cc: Theresa Jayne Forster
Subject: Re: Problem that could lose me my job

 

On 3/02/2012 8:58 PM, Theresa Jayne Forster wrote: 

Sorry, I was under pressure to roll back the servers that we had just
upgraded to use the fop 1.0 system. 

One of the failing PDFs is at http://www.inbrand1.co.uk/failsinadobe.pdf

The FO that generated it is at http://www.inbrand1.co.uk/failsinadobe.fo

 

"Adobe" (or even "Adobe Acrobat" or "Adobe Reader") can be a lot of
different things. Versions are important.

That said, the PDF is clearly broken. Adobe Reader X fails with "There was
an error opening this document. The file is damaged and could not be
repaired". It isn't handled by Chrome either.

Re David's comment about the XSL-FO being non-confirming: If fop produces a
broken PDF silently and without error output that is a fop bug, even if it's
triggered by bad XSL-FO input. A warning should be emitted if valid PDF can
be produced - though possibly not displaying how the author intended - but
if no valid PDF can be produced that surely must be a fatal error. I know
you're not actually saying it's *not* a fop bug, just being clear that IMO
bad FO -> bad PDF *is* a fop bug.

However, looking at your code below, I see little reason to believe this is
a fop bug. All you've shown so far is that your code ignores errors if fop
does tell you about them. Have you looked at your application's error logs
to see if fop is throwing an exception and if so, what? That's the only
place you'll see errors since your code otherwise ignores them:




        } catch (IOException ioe) { 

            LOGGER.error("IOException ",ioe);

            ioe.printStackTrace();

        } catch (SAXException se) {

            LOGGER.error("Sax Exception ",se);

            se.printStackTrace();

        } catch (ConfigurationException ce) {

            LOGGER.error("Configuration Exception ",ce);

            ce.printStackTrace();

        } catch (TransformerConfigurationException tce) {

            LOGGER.error("Transformer Configuration Exception",tce);

            tce.printStackTrace();

        } catch (TransformerException te) {

            LOGGER.error("Transformer Exception ",te);

            te.printStackTrace();

        }

        return tempFile;

 


... that's some "interesting" error handling code. LOGGER clearly doesn't
throw another (wrapping) exception since otherwise `te.printStackTrace()'
would never be getting executed. That means your code is logging an error
(two different ways) then IGNORING IT.

Not only is that error handling code rather skeezy, but you assign a value
to tempFile early on and do not clear it when an error occurs, so you MAY
RETURN A HALF-WRITTEN GARBAGE FILE TO THE CALLER if an error occurs.

I strongly advise you to fix your error handling code and re-test. There are
at least a few bugs in that code:

- After some errors you may return a handle to an incompletely written temp
file without any indication it's broken

- You don't close your file handle(s) or streams, so you're relying on the
objects' finialisation to do it for you when the gc gets
  around to cleaning them up. This is not usually a good idea. You really
need to be using try{}finally{} blocks to clean up
  resources like this.

Try correcting your error handling code and seeing if the problem changes.

Please also send any messages fop 1.0 logs with detailed logging enabled
when it is producing the faulty PDF. I'm sure you've looked at the logs as
part of basic first-step troubleshooting anyway, so you should have no
problem bringing them to hand...

Here's a 30-second quickly hacked out version of how I'd want to tweak this.
I haven't tested this code and don't claim I'm any expert, I'm just sure
it's closer than what you're using right now:

    // Of course you'd use your app's real exception classes instead of this
demo
    // scratch code....
    public class MyApplicationRuntimeException extends RuntimeException {
        public MyApplicationRuntimeException(Throwable err) { super(err); }
        public MyApplicationRuntimeException(String msg) { super(msg); }
        public MyApplicationRuntimeException(String msg, Throwable err) {
super(msg,err); }
    }

    public File generatePDFromFO(File foFile) {
        File tempFile = File.createTempFile("W2P", ".pdf");
        try {
            fopFactory.setStrictValidation(false);  // <--  This is a
**HINT** you're doing something **WRONG**
            DefaultConfigurationBuilder cfgBuilder = new
DefaultConfigurationBuilder();
            Configuration cfg = cfgBuilder.buildFromFile(new
File("/fopconfig.xml"));  // <-- You're reparsing and re-applying the config
for each file?!?
            fopFactory.setUserConfig(cfg);
            FOUserAgent useragent = fopFactory.newFOUserAgent();
            useragent.setTargetResolution(300);
            TransformerFactory factory = TransformerFactory.newInstance();
            BufferedOutputSteam out = new BufferedOutputStream(new
FileOutputStream(tempFile));
            try {
                Fop fop =
fopFactory.newFop(MimeConstants.MIME_PDF,useragent,out);
                Source src = new StreamSource(foFile);
                Transformer transformer = factory.newTransformer();
                Result res = new SAXResult(fop.getDefaultHandler());
                transformer.transform(src, res);
                return tempFile;
            } finally {
                // Always close the output stream when we're done.
                //
                // See: Apache IOUtils. You can do the same thing more
verbosely
                // by hand with another try/catch block.
                IOUtils.closeQuietly(out);
            }
        // For the following I'd use a Java 7 multi-catch to reduce the
repetition
        // or if limited to Java <= 6 I'd assign the exception to a local
variable
        // and fall out of the catch  in each clause, so I could use the
same 3 lines
        // once at the end of the method to handle all error cases together.
Up to you
        // though, that's just style preference so I've left it how you had
it.
        } catch (IOException ioe) {
            tempFile.delete(); // Get rid of the failed tempfile if it was
created, ignoring error
            LOGGER.error("IOException ",ioe);
            throw new MyApplicationRuntimeException("I/O error while
producing PDF from XSL-FO", ioe);
        } catch (SAXException se) {
            tempFile.delete();
            LOGGER.error("Sax Exception ",se);
            throw new MyApplicationRuntimeException("SAX XML error while
producing PDF from XSL-FO", ioe);
        } catch (ConfigurationException ce) {
            tempFile.delete();
            LOGGER.error("Configuration Exception ",ce);
            throw new MyApplicationRuntimeException("Fop configuration error
while producing PDF from XSL-FO", ioe);
        } catch (TransformerConfigurationException tce) {
            tempFile.delete();
            LOGGER.error("Transformer Configuration Exception",tce);
            throw new MyApplicationRuntimeException("XML Tranformer
configuration error while producing PDF from XSL-FO", ioe);
        } catch (TransformerException te) {
            tempFile.delete();
            LOGGER.error("Transformer Exception ",te);
            throw new MyApplicationRuntimeException("XML transformer error
while producing PDF from XSL-FO", ioe);
        }
    } 
  }

Reply via email to