[
https://issues.apache.org/jira/browse/PDFBOX-4242?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16521927#comment-16521927
]
Emmeran Seehuber commented on PDFBOX-4242:
------------------------------------------
[~tilman] Yes, using a finalizer is a no go, as it will likely never run. The
way to go here is to use a PhantomReference and a ReferenceQueue to close the
file handle. E.g.
{code:java}
class TTFPhantomReference extends PhantomReference<TrueTypeFont> {
TTFPhantomReference(TrueTypeFont font) {
super(font, TTFReferenceQueue.INSTANCE);
dataToClose = font.data;
// Pin this reference, otherwise it will be GCed before it can do it's
magic...
TTFReferenceQueue.referencePin.add(this);
}
TTFDataStream dataToClose;
}
class TTFReferenceQueue {
static final TTFReferenceQueue INSTANCE = new TTFReferenceQueue();
/* Pin list, to not have the Referencer GCed before it could cleanup the
objects */
ConcurrentLinkedDeque<TTFPhantomReference> referencePin = new
ConcurrentLinkedDeque<TTFPhantomReference>();
/* The ugly part, we need a thread to poll the queue */
static final Thread ttfPollThread = new Thread(){
public void run(){
while(true) {
// Block till we get a reference.
TTFPhantomReference ref = INSTANCE.remove();
if(ref != null ) {
ref.close();
referencePin.remove(ref);
}
}
}
};
static {
ttfPollThread.setDeamon(true);
ttfPollThread.start();
}
}
{code}
No idea if the TrueTypeFont should be the object that is referenced in the
PhantomReference, or if it should be a parent object (e.g. PDFont). The Phantom
Reference just needs a reference to the resource that should be closed but of
course not a reference to the owner object. This avoids the finalizer()
resurrection problem, as the reference object will already be gone.
The problem with this code is: It creates a static thread, which in turn may
lead to a class loader leak.
Guava does some special magic to avoid this problem and make it work correctly
with container environments. If you could use Guava you would just make this
code look like:
{code:java}
public class TTFFileCloser {
private static FinalizableReferenceQueue queue = new
FinalizableReferenceQueue();
private ConcurrentLinkedDeque<FinalizablePhantomReference<TrueTypeFont>>
phantomReferencePinner = new ConcurrentLinkedDeque<>();
public void closeFileIfNotReachable(TrueTypeFont owner, final TTFDataStream
data) {
FinalizablePhantomReference<TrueTypeFont> finalizablePhantomReference = new
FinalizablePhantomReference<TrueTypeFont>(owner, queue) {
@Override
public void finalizeReferent() {
data.close();
phantomReferencePinner.remove(this);
}
};
// Pin the reference to avoid it beeing GCed
phantomReferencePinner.add(finalizablePhantomReference);
}
}
{code}
PhantomReferences are queued very fast after the owner object is no longer
reachable. I use this (with the Guava base classes) very successful to cleanup
all my temp files. As you can't use Guava I don't know if you want to go this
route, as getting this right in a servlet context is not that easy... Note: You
don't need to create a background thread to do the cleanup, Guava has a
fallback that it just will cleanup all references that are queued if a new
reference is added to the list. But it only uses this if it can't create a
background thread.
> Fontbox does not close file descriptor when loading fonts.
> ----------------------------------------------------------
>
> Key: PDFBOX-4242
> URL: https://issues.apache.org/jira/browse/PDFBOX-4242
> Project: PDFBox
> Issue Type: Bug
> Components: FontBox
> Affects Versions: 2.0.9
> Reporter: Glen Peterson
> Priority: Minor
> Labels: file_leak
>
> My app has been getting "java.io.FileNotFoundException (No file descriptors
> available)" and I've confirmed that it's because fontbox isn't closing it's
> file descriptors.
> In org.apache.fontbox.ttf.TTFParser there's this method:
> {{public TrueTypeFont parse(File ttfFile) throws IOException {}}
> {{ RAFDataStream raf = new RAFDataStream(ttfFile, "r");}}
> {{ try {}}
> {{ return this.parse((TTFDataStream)raf);}}
> {{ } catch (IOException var4) {}}
> {{ // close only on error (file is still being accessed later)}}
> {{ raf.close();}}
> {{ throw var4;}}
> {{}}}
> {{}}}
> I would have expected to see the close() in a finally block so that the file
> is always closed, not just on exceptions. Presumably, you can keep it in
> memory without leaving the file descriptor open?
> {{public TrueTypeFont parse(File ttfFile) throws IOException {}}
> {{ RAFDataStream raf = new RAFDataStream(ttfFile, "r");}}
> {{ try {}}
> {{ return this.parse((TTFDataStream)raf);}}
> {{ } catch (IOException var4) {}}{{ raf.close();}}
> {{ throw var4;}}
> {{ } finally {}}
> {{ raf.close();}}
> {{}}}
> {{}}}
> I tried performing this in a lazy initialization, but it blew up:
> java.lang.RuntimeException: java.io.IOException: The TrueType font null does
> not contain a 'cmap' tableCaused by: java.io.IOException: The TrueType font
> null does not contain a 'cmap' table
> at
> org.apache.fontbox.ttf.TrueTypeFont.getUnicodeCmapImpl(TrueTypeFont.java:548)
> at
> org.apache.fontbox.ttf.TrueTypeFont.getUnicodeCmapLookup(TrueTypeFont.java:528)
> at
> org.apache.fontbox.ttf.TrueTypeFont.getUnicodeCmapLookup(TrueTypeFont.java:514)
> at org.apache.fontbox.ttf.TTFSubsetter.<init>(TTFSubsetter.java:91)
> at
> org.apache.pdfbox.pdmodel.font.TrueTypeEmbedder.subset(TrueTypeEmbedder.java:321)
> at org.apache.pdfbox.pdmodel.font.PDType0Font.subset(PDType0Font.java:239)
> at org.apache.pdfbox.pdmodel.PDDocument.save(PDDocument.java:1271)
> Thoughts?
> Thanks for PDFBox - it's been really helpful!
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]