JDK-8152423 : Generated temp files (+JXF...temp) for custom fonts not deleted on exit.
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 8u74,9
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2016-03-16
  • Updated: 2017-09-07
  • Resolved: 2016-05-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u112Fixed 9Fixed
Related Reports
Duplicate :  
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Win 7 x64

A DESCRIPTION OF THE PROBLEM :
Generated temp files (+JXF...temp) for custom fonts not deleted on exit.



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached code with font "ARIAL.ttf"

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The generated temp font file "[user_home]/AppData/Local/Temp/+JXF....tmp" deleted on exit
ACTUAL -
Temp file not deleted.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No error message.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.IOException;
import java.io.InputStream;

import javax.naming.ConfigurationException;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class FxFontApplication extends Application {

	private static final String fontName = "ARIAL.ttf";

	public static void main(String[] args) {
		launch(args);
	}

	@Override
	public void start(Stage primaryStage) throws ConfigurationException, IOException {

		loadFontForFX();

		StackPane root = new StackPane();
		root.getChildren().add(new Label("Hello Font"));

		Scene scene = new Scene(root, 300, 200, javafx.scene.paint.Color.ALICEBLUE);
		// No difference with or without css
		// scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());

		primaryStage.setScene(scene);
		primaryStage.show();
	}

	private void loadFontForFX() throws ConfigurationException, IOException {

		InputStream inputStream = FxFontApplication.class.getResourceAsStream(fontName);
		javafx.scene.text.Font loadedFont = javafx.scene.text.Font.loadFont(inputStream, -1);
		if (loadedFont == null) {
			throw new ConfigurationException("Cannot load font for JavaFX from file '" + fontName + "'.");
		}
		inputStream.close();
	}

}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
No workaround found.


Comments
Thanks for the confirmation. Approved to backport this to 8u-dev (8u112)
17-05-2016

Yes it applies cleanly
17-05-2016

I agree with the comment on the newly-created backport. This does seem like a good candidate for a backport. I presume the 9-dev changeset applies cleanly to 8u-dev?
15-05-2016

Changeset: 8d3a930fc4b9 Author: prr Date: 2016-05-13 15:00 -0700 URL: http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/8d3a930fc4b9 8152423: Generated temp files (+JXF...temp) for custom fonts not deleted on exit. Reviewed-by: flar, kcr ! modules/graphics/src/main/java/com/sun/javafx/font/PrismFontFile.java ! modules/graphics/src/main/java/com/sun/javafx/font/directwrite/DWFontFile.java
13-05-2016

Looks good. +1
13-05-2016

I don't pretend to understand the detail about isRegistered vs. copy as a test to enable this, but the deletion stuff looks fine to me...
13-05-2016

The fix has been updated as Jim suggested ... http://cr.openjdk.java.net/~prr/8152423.1 .. although I then discovered that the code was mistakenly registering a disposer only for fonts that do not get "registered",. Being registered means it shows up in the list of of fonts known to the app. Webkit fonts don't register and were the only ones getting a disposer. Oops. So I fixed it to check "copy" instead. In which case I decided to first try the disposer and if for some reason (that in theory should not happen) it does not exist then directly call Release so as to avoid leaking files. The leftover files noted by Kevin occur only when the TEMP directory is somewhere other than \Users\<XXX>. Since the default is \Users\<XXX>\AppData\Local\Temp this is not usually an issue. Kevin was running from a cygwin shell which had set it to \cygdrive\c\tmp. Digging into this has so far confirmed only that delete reports it could not delete the file, presumably because it is still in use. Directwrite makes use of a System Font Cache service and my guess is the font cache is what is referencing the file. And probably in the case of the user directory the cache service does not trust that location and copies the data somewhere else first. And in the non-user directory case it just uses the file directly - as it would for fonts in \windows\fonts. So a complete solution to this would be a reworking so that we never provide DirectWrite with direct access to the file by implementing interfaces that it calls to get font data instead This would either force that copy or bypass the font cache. But for now I am just opting for the quick fix to the normal case and Kevin has OKed this. I have added a debugging message which will report when the font file could not be deleted.
13-05-2016

I can confirm that this fixes the attached test case on my machine, but some of our (closed) unit tests still leave leftovers.
05-05-2016

This looks good!
05-05-2016

> Well, calling fontFace.Release() does the trick. Yes, but it should be called in the same way that the (closed source) code does by over-riding disposeOnShutdown in the DWFontFile subclass and then calling super() to delete the font file. The following as described fixes the issue for me. http://cr.openjdk.java.net/~prr/8152423/
05-05-2016

Well, calling fontFace.Release() does the trick. The solution is not really elegant and I'm sure there's a better way, but here's at least something which works: http://cr.openjdk.java.net/~vadim/8152423/webrev.00/
01-04-2016

Yes, this is what I expected. There is a an method which calls "Release" which seems to be the peer of the call to CreateFontFileReference and either it is not being called, or it doesn't do what it ought to do. I have not yet had time to dig into it. I didn't find any other obvious method we should have been calling. There is some DW API to let you provide the FontFileLoader so it goes via that and not directly to the file. I hope to avoid that but if DW doesn't release the file we may need to try that.
01-04-2016

It seems that DirectWrite holds an open handle to the temporary file, preventing its deletion from the disposeOnShutdown method. Specifically, the handle remains open after the call to IDWriteFontFile::Analyze, at least this is what can be seen from the procmon dump.
01-04-2016

Windows-only strongly implies that the filecloser isn't working properly and when I look at the fileCloser hook I don't see where it tries to close the file. Interestingly if I pass -Dprism.text=t2k the temp file is cleaned up, which suggests it is a regression in the native rasteriser path, and the t2k code path is either explicitly closing it, or does not have the file open.
28-03-2016

FWIW, I have many of these leftover in my /tmp directory each time I run our unit tests.
28-03-2016

Looks like this is a Windows-only problem. I ran the unit tests on Mac and Linux and do not see any leftovers.
28-03-2016

I was able to reproduce on win7 using 8u74 64 bit.
25-03-2016

Hmm .. we do have code that is supposed to do this clean-up.
25-03-2016