Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6359722

Uncatchable recursive NullPointerException at sun.font.TrueTypeFont.open()

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P5 P5
    • 7
    • 5.0
    • client-libs
    • 2d
    • 5.0
    • b03
    • x86
    • linux
    • Verified

        FULL PRODUCT VERSION :
        java version "1.5.0_05"
        Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05)
        Java HotSpot(TM) Client VM (build 1.5.0_05-b05, mixed mode, sharing)

        ADDITIONAL OS VERSION INFORMATION :
        Linux 2.4.21-37.ELsmp #1 SMP Wed Sep 7 13:28:55 EDT 2005 i686 i686 i386 GNU/Linux

        A DESCRIPTION OF THE PROBLEM :
        Our java server (using intensively font files) was freezing almost every day reporting from time to time the recursive following stack trace in the log file :

        2005-11-16 11:51:23,700 ERROR [STDERR] java.awt.FontFormatException: java.lang.NullPointerException
        2005-11-16 11:51:23,700 ERROR [STDERR] at sun.font.TrueTypeFont.open(TrueTypeFont.java:251)
        2005-11-16 11:51:23,700 ERROR [STDERR] at sun.font.TrueTypeFont.readBlock(TrueTypeFont.java:279)
        2005-11-16 11:51:23,700 ERROR [STDERR] at sun.font.FileFont.getGlyphAdvance(Native Method)
        2005-11-16 11:51:23,700 ERROR [STDERR] at sun.font.FileFontStrike.getGlyphAdvance(FileFontStrike.java:491)
        2005-11-16 11:51:23,701 ERROR [STDERR] at sun.font.FileFontStrike.getCodePointAdvance(FileFontStrike.java:502)
        2005-11-16 11:51:23,701 ERROR [STDERR] at sun.font.FontDesignMetrics.handleCharWidth(FontDesignMetrics.java:226)
        2005-11-16 11:51:23,701 ERROR [STDERR] at sun.font.FontDesignMetrics.getLatinCharWidth(FontDesignMetrics.java:235)
        2005-11-16 11:51:23,701 ERROR [STDERR] at sun.font.FontDesignMetrics.stringWidth(FontDesignMetrics.java:285)
        2005-11-16 11:51:23,701 ERROR [STDERR] at com.sun.java.swing.SwingUtilities2.stringWidth(SwingUtilities2.java:292)
        2005-11-16 11:51:23,701 ERROR [STDERR] at javax.swing.SwingUtilities.computeStringWidth(SwingUtilities.java:761)
        ...

        We couldn't reproduce this behaviour on our test server until we tried this simple test : start the server and run a scenario using font files, then delete the .tmp file generated in the jvm temp directory.
        Apparently, this leads to the error we had on the production server. For some reason, the tmp directory is cleaned up by some daemon. Though we still have to investigate about this linux daemon which should not run, the error generated in the jvm can not be recovered and finally freezes the server which should not normally happen.

        Indeed, the open or readBlock method is synchronized and any other thread trying to use font files even if the tmp file still exists is stuck by the one that tries indefinitely to write error logs because of the NullPointerException.


        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Start the server and run a scenario using font files, then delete the .tmp file generated in the jvm temp directory.


        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        If the font tmp file was deleted : throw an exception catchable by the calling thread.
        ACTUAL -
        Recursive stack traces in the log file + blocked threads + finally server freeze.

        ERROR MESSAGES/STACK TRACES THAT OCCUR :
        :

        2005-11-16 11:51:23,700 ERROR [STDERR] java.awt.FontFormatException: java.lang.NullPointerException
        2005-11-16 11:51:23,700 ERROR [STDERR] at sun.font.TrueTypeFont.open(TrueTypeFont.java:251)
        2005-11-16 11:51:23,700 ERROR [STDERR] at sun.font.TrueTypeFont.readBlock(TrueTypeFont.java:279)
        2005-11-16 11:51:23,700 ERROR [STDERR] at sun.font.FileFont.getGlyphAdvance(Native Method)
        2005-11-16 11:51:23,700 ERROR [STDERR] at sun.font.FileFontStrike.getGlyphAdvance(FileFontStrike.java:491)
        2005-11-16 11:51:23,701 ERROR [STDERR] at sun.font.FileFontStrike.getCodePointAdvance(FileFontStrike.java:502)
        2005-11-16 11:51:23,701 ERROR [STDERR] at sun.font.FontDesignMetrics.handleCharWidth(FontDesignMetrics.java:226)
        2005-11-16 11:51:23,701 ERROR [STDERR] at sun.font.FontDesignMetrics.getLatinCharWidth(FontDesignMetrics.java:235)
        2005-11-16 11:51:23,701 ERROR [STDERR] at sun.font.FontDesignMetrics.stringWidth(FontDesignMetrics.java:285)
        2005-11-16 11:51:23,701 ERROR [STDERR] at com.sun.java.swing.SwingUtilities2.stringWidth(SwingUtilities2.java:292)
        2005-11-16 11:51:23,701 ERROR [STDERR] at javax.swing.SwingUtilities.computeStringWidth(SwingUtilities.java:761)
        ...



        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
        Using this fonction in loop on several fonts and images while deleting the tmp font files in the jvm tmp dir :

            private static boolean checkWidth(String text, Font font, Graphics graphics, int imgWidth) {
                final FontMetrics fm = graphics.getFontMetrics(font);
                final int width = SwingUtilities.computeStringWidth(fm, text);
                return imgWidth > width;
            }
        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        Using our own java.io.tmpdir directory and cleaning up the tmp files manually when the corresponding font file is not used any more solved the problem.
        Here is a piece of code that produces the exception.
        You need to place a directory called 'font' with more than 20 ttf fonts in
        it (so at least 21) at the same level you compile and run this code (i don't
        know if our fonts are special but I guess you can reproduce the problem with
        any font files).
        Compile and just run the test :
        java -cp . CheckFontWidth

        /*
         * Copyright 2004 Mediabilis S.A.S., 135 rue de Billancourt,
         * 92514 Boulogne-Billancourt, France
         * www.mediabilis.com, ###@###.###
         * All Rights Reserved.
         *
         * Created on 29 oct. 2004
         */
        import java.awt.Font;
        import java.awt.FontFormatException;
        import java.awt.image.BufferedImage;
        import java.io.File;
        import java.io.FileInputStream;
        import java.io.FilenameFilter;
        import java.io.IOException;
        import java.io.InputStream;
        import java.util.HashMap;
        import java.util.Map;

        import javax.swing.SwingUtilities;


        /**
         *
         * @version 1.0 29 oct. 2004
         * @author j.guyard
         */
        public class CheckFontWidth {
            
            // Font cache map
            private static Map fonts;
            
            public static void main(String[] args) {
                try {
                    // Init font cache
                    fonts = new HashMap();
                    // list font file names
                    File fontdir = new File("font");
                    String[] fontStrs = fontdir.list();
                    System.out.println("Nb of fonts = " + fontStrs.length);
                    Font font = null;
                    int i = 0;
                    BufferedImage img = new
        BufferedImage(10,10,BufferedImage.TYPE_INT_RGB);
                    while (true)
                    {
                        System.out.println("i = " + i);
                        // get and cache next font
                        font = getFont("font/" + fontStrs[i++]);
                        
                        // Use a text that changes (hour for instance)
                        String text = String.valueOf(System.currentTimeMillis());
                        
                        // Compute width
                        int width =
        SwingUtilities.computeStringWidth(img.createGraphics().getFontMetrics(font),
        text);
                        
                        // Reset counter and delete tmp file
                        if (i == (fontStrs.length)) {
                            System.out.println("Reset counter and delete tmp
        file.");
                            i = 0;
                            File tmp = new
        File(System.getProperties().getProperty("java.io.tmpdir"));
                            if (tmp.exists()) {
                                File[] tmpFiles = tmp.listFiles(new
        FilenameFilter(){
                                    public boolean accept(File file, String str) {
                                        final String [] ext = str.split("[.]");
                                        if (ext.length >= 2) {
                                            return ext[ext.length-1].equals("tmp");
                                        } else {
                                            return false;
                                        }
                                    }
                                });
                                for (int j = 0; j < tmpFiles.length; j++) {
                                    File file = tmpFiles[j];
                                    file.delete();
                                }
                            }
                           
                        }
                    }
                    
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (FontFormatException e) {
                    e.printStackTrace();
                }
            }
            
            // Font loading method
            public static Font getFont(String path) throws IOException,
        FontFormatException {
                final File fontFile = new File(path);
                final Font font;
                if (!fonts.containsKey(path)) {
                    InputStream fontStream = null;
                    try {
                        fontStream = new FileInputStream(path);
                        font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
                    } finally {
                        if (null != fontStream) {
                            try {
                                fontStream.close();
                            } catch (IOException ioe) {
                            }
                        }
                    }
                    fonts.put(path, font);
                } else {
                    font = (Font) fonts.get(path);
                }
                return font;
            }
            
        }

              prr Philip Race
              prr Philip Race
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: