1 /* 2 * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.font; 27 28 import java.io.File; 29 import java.io.OutputStream; 30 import java.security.AccessController; 31 import java.security.PrivilegedAction; 32 import java.util.HashMap; 33 import java.util.Map; 34 import java.util.concurrent.Semaphore; 35 import java.util.concurrent.TimeUnit; 36 37 import sun.awt.AppContext; 38 import sun.awt.util.ThreadGroupUtils; 39 40 public class CreatedFontTracker { 41 42 public static final int MAX_FILE_SIZE = 32 * 1024 * 1024; 43 public static final int MAX_TOTAL_BYTES = 10 * MAX_FILE_SIZE; 44 45 static CreatedFontTracker tracker; 46 int numBytes; 47 48 public static synchronized CreatedFontTracker getTracker() { 49 if (tracker == null) { 50 tracker = new CreatedFontTracker(); 51 } 52 return tracker; 53 } 54 55 private CreatedFontTracker() { 56 numBytes = 0; 57 } 58 59 public synchronized int getNumBytes() { 60 return numBytes; 61 } 62 63 public synchronized void addBytes(int sz) { 64 numBytes += sz; 65 } 66 67 public synchronized void subBytes(int sz) { 68 numBytes -= sz; 69 } 70 71 /** 72 * Returns an AppContext-specific counting semaphore. 73 */ 74 private static synchronized Semaphore getCS() { 75 final AppContext appContext = AppContext.getAppContext(); 76 Semaphore cs = (Semaphore) appContext.get(CreatedFontTracker.class); 77 if (cs == null) { 78 // Make a semaphore with 5 permits that obeys the first-in first-out 79 // granting of permits. 80 cs = new Semaphore(5, true); 81 appContext.put(CreatedFontTracker.class, cs); 82 } 83 return cs; 84 } 85 86 public boolean acquirePermit() throws InterruptedException { 87 // This does a timed-out wait. 88 return getCS().tryAcquire(120, TimeUnit.SECONDS); 89 } 90 91 public void releasePermit() { 92 getCS().release(); 93 } 94 95 public void add(File file) { 96 TempFileDeletionHook.add(file); 97 } 98 99 public void set(File file, OutputStream os) { 100 TempFileDeletionHook.set(file, os); 101 } 102 103 public void remove(File file) { 104 TempFileDeletionHook.remove(file); 105 } 106 107 /** 108 * Helper class for cleanup of temp files created while processing fonts. 109 * Note that this only applies to createFont() from an InputStream object. 110 */ 111 private static class TempFileDeletionHook { 112 private static HashMap<File, OutputStream> files = new HashMap<>(); 113 114 private static Thread t = null; 115 static void init() { 116 if (t == null) { 117 // Add a shutdown hook to remove the temp file. 118 AccessController.doPrivileged( 119 (PrivilegedAction<Void>) () -> { 120 /* The thread must be a member of a thread group 121 * which will not get GCed before VM exit. 122 * Make its parent the top-level thread group. 123 */ 124 ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup(); 125 t = new Thread(rootTG, TempFileDeletionHook::runHooks); 126 t.setContextClassLoader(null); 127 Runtime.getRuntime().addShutdownHook(t); 128 return null; 129 }); 130 } 131 } 132 133 private TempFileDeletionHook() {} 134 135 static synchronized void add(File file) { 136 init(); 137 files.put(file, null); 138 } 139 140 static synchronized void set(File file, OutputStream os) { 141 files.put(file, os); 142 } 143 144 static synchronized void remove(File file) { 145 files.remove(file); 146 } 147 148 static synchronized void runHooks() { 149 if (files.isEmpty()) { 150 return; 151 } 152 153 for (Map.Entry<File, OutputStream> entry : files.entrySet()) { 154 // Close the associated output stream, and then delete the file. 155 try { 156 if (entry.getValue() != null) { 157 entry.getValue().close(); 158 } 159 } catch (Exception e) {} 160 entry.getKey().delete(); 161 } 162 } 163 } 164 }