We are running a PD4ML Pro 323, which I am aware of is a bit old. Lately we had an incident where arbitrary requests/responses were mixed up, that is users seeing each other's data! Being a web application that requires login and uses HTTPS to protect our user's data that is very serious business. The problem was traced back to our usage of PD4ML.
PD4ML.render(StringReader, OutputStream) was called from a web application, and HttpServletResponse.getOutputStream() was used as the second argument. It turns out that PD4ML closes the OutputStream, and does it twice. Once from within PD4ML.render() via PD4Device.dispose(). That is unexpected (since the usual contract is that the one who opened a stream is the one responsible for closing it), but it does not do any harm in our case. The second close comes from PD4Device.finalize(), and that is the harmful one! The finalize method will be invoked from another thread, the JVM FinalizerThread, at some later time in the future. It means that the ResponseOutputStream gets an unexpected close() call from an external thread, which is not allowed by the Servlet specification.
We are using Apache + Tomcat with an AJP connector. In that case, the OutputStream returned by response.getOutputStream() is a org.apache.catalina.connector.CoyoteOutputStream, which is a thin wrapper dispatching directly to an org.apache.catalina.connector.OutputBuffer. The problem is that the latter is reused by the web container. So when the stream.close() comes from PD4Device, it is probably already pooled for reuse or even in use by a subsequent request! That unexpected close causes the connector and/or Apache mod_jk to mismatch request/response on that socket. Exactly how this bit happen has not been possible to debug yet. But clearly the web container makes no guarantees if the response stream is accessed out of scope.
If I wrap the ServletOutputStream with a FilterOutputStream that makes all close() calls ignored, the bug disappears at once. That is how the call from finalize was found.
Code:
at org.zefer.pd4ml.pdf.PD4Device.dispose(Unknown Source)
at org.zefer.pd4ml.pdf.PD4Device.finalize(Unknown Source)
at java.lang.ref.Finalizer.invokeFinalizeMethod(Native Method)
at java.lang.ref.Finalizer.runFinalizer(Finalizer.java:101)
at java.lang.ref.Finalizer.access$100(Finalizer.java:32)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:178)
Why would you close a stream in finalize()? A stream that you did not even open and thus were not responsible for closing? I know there are several releases past 323, but since we are currently not paying for updates I can't tell if the bug remains or not. If it does it should be fixed, or you must post big warning signs in the javadoc that no one must ever call PD4ML.render() with a ServletOutputStream as argument.