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.util.HashMap; 31 import java.util.Map; 32 import java.util.concurrent.Semaphore; 33 import java.util.concurrent.TimeUnit; 34 35 import sun.awt.AppContext; 36 37 public class CreatedFontTracker { 38 39 public static final int MAX_FILE_SIZE = 32 * 1024 * 1024; 40 public static final int MAX_TOTAL_BYTES = 10 * MAX_FILE_SIZE; 41 42 static CreatedFontTracker tracker; 43 int numBytes; 44 45 public static synchronized CreatedFontTracker getTracker() { 46 if (tracker == null) { 47 tracker = new CreatedFontTracker(); 48 } 49 return tracker; 50 } 51 52 private CreatedFontTracker() { 53 numBytes = 0; 54 } 55 56 public synchronized int getNumBytes() { 57 return numBytes; 58 } 59 60 public synchronized void addBytes(int sz) { 61 numBytes += sz; 62 } 63 64 public synchronized void subBytes(int sz) { 65 numBytes -= sz; 66 } 67 68 /** 69 * Returns an AppContext-specific counting semaphore. 70 */ 71 private static synchronized Semaphore getCS() { 72 final AppContext appContext = AppContext.getAppContext(); 73 Semaphore cs = (Semaphore) appContext.get(CreatedFontTracker.class); 74 if (cs == null) { 75 // Make a semaphore with 5 permits that obeys the first-in first-out 76 // granting of permits. 77 cs = new Semaphore(5, true); 78 appContext.put(CreatedFontTracker.class, cs); 79 } 80 return cs; 81 } 82 83 public boolean acquirePermit() throws InterruptedException { 84 // This does a timed-out wait. 85 return getCS().tryAcquire(120, TimeUnit.SECONDS); 86 } 87 88 public void releasePermit() { 89 getCS().release(); 90 } 91 92 public void add(File file) { 93 TempFileDeletionHook.add(file); 94 } 95 96 public void set(File file, OutputStream os) { 97 TempFileDeletionHook.set(file, os); 98 } 99 100 public void remove(File file) { 101 TempFileDeletionHook.remove(file); 102 } 103 104 /** 105 * Helper class for cleanup of temp files created while processing fonts. 106 * Note that this only applies to createFont() from an InputStream object. 107 */ 108 private static class TempFileDeletionHook { 109 private static HashMap<File, OutputStream> files = new HashMap<>(); 110 111 private static Thread t = null; 112 static void init() { 113 if (t == null) { 114 // Add a shutdown hook to remove the temp file. 115 java.security.AccessController.doPrivileged( 116 new java.security.PrivilegedAction<Object>() { 117 public Object run() { 118 /* The thread must be a member of a thread group 119 * which will not get GCed before VM exit. 120 * Make its parent the top-level thread group. 121 */ 122 ThreadGroup tg = 123 Thread.currentThread().getThreadGroup(); 124 for (ThreadGroup tgn = tg; 125 tgn != null; 126 tg = tgn, tgn = tg.getParent()); 127 t = new Thread(tg, new Runnable() { 128 public void run() { 129 runHooks(); 130 } 131 }); 132 t.setContextClassLoader(null); 133 Runtime.getRuntime().addShutdownHook(t); 134 return null; 135 } 136 }); 137 } 138 } 139 140 private TempFileDeletionHook() {} 141 142 static synchronized void add(File file) { 143 init(); 144 files.put(file, null); 145 } 146 147 static synchronized void set(File file, OutputStream os) { 148 files.put(file, os); 149 } 150 151 static synchronized void remove(File file) { 152 files.remove(file); 153 } 154 155 static synchronized void runHooks() { 156 if (files.isEmpty()) { 157 return; 158 } 159 160 for (Map.Entry<File, OutputStream> entry : files.entrySet()) { 161 // Close the associated output stream, and then delete the file. 162 try { 163 if (entry.getValue() != null) { 164 entry.getValue().close(); 165 } 166 } catch (Exception e) {} 167 entry.getKey().delete(); 168 } 169 } 170 } 171 }