1 /* 2 * Copyright (c) 1997, 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 com.sun.xml.internal.org.jvnet.mimepull; 27 28 import java.util.concurrent.TimeUnit; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.io.RandomAccessFile; 33 import java.lang.ref.ReferenceQueue; 34 import java.lang.ref.WeakReference; 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.concurrent.ScheduledExecutorService; 38 import java.util.logging.Level; 39 import java.util.logging.Logger; 40 41 /** 42 * Removing files based on this 43 * <a href="http://java.sun.com/developer/technicalArticles/javase/finalization/">article</a> 44 * 45 * @author Jitendra Kotamraju 46 */ 47 final class WeakDataFile extends WeakReference<DataFile> { 48 49 private static final Logger LOGGER = Logger.getLogger(WeakDataFile.class.getName()); 50 private static int TIMEOUT = 10; //milliseconds 51 //private static final int MAX_ITERATIONS = 2; 52 private static ReferenceQueue<DataFile> refQueue = new ReferenceQueue<DataFile>(); 53 private static List<WeakDataFile> refList = new ArrayList<WeakDataFile>(); 54 private final File file; 55 private final RandomAccessFile raf; 56 private static boolean hasCleanUpExecutor = false; 57 static { 58 int delay = 10; 59 try { 60 delay = Integer.getInteger("com.sun.xml.internal.org.jvnet.mimepull.delay", 10); 61 } catch (SecurityException se) { 62 if (LOGGER.isLoggable(Level.CONFIG)) { 63 LOGGER.log(Level.CONFIG, "Cannot read ''{0}'' property, using defaults.", 64 new Object[] {"com.sun.xml.internal.org.jvnet.mimepull.delay"}); 65 } 66 } 67 CleanUpExecutorFactory executorFactory = CleanUpExecutorFactory.newInstance(); 68 if (executorFactory!=null) { 69 if (LOGGER.isLoggable(Level.FINE)) { 70 LOGGER.log(Level.FINE, "Initializing clean up executor for MIMEPULL: {0}", executorFactory.getClass().getName()); 71 } 72 ScheduledExecutorService scheduler = executorFactory.getScheduledExecutorService(); 73 scheduler.scheduleWithFixedDelay(new CleanupRunnable(), delay, delay, TimeUnit.SECONDS); 74 hasCleanUpExecutor = true; 75 } 76 } 77 78 WeakDataFile(DataFile df, File file) { 79 super(df, refQueue); 80 refList.add(this); 81 this.file = file; 82 try { 83 raf = new RandomAccessFile(file, "rw"); 84 } catch(IOException ioe) { 85 throw new MIMEParsingException(ioe); 86 } 87 if (!hasCleanUpExecutor) { 88 drainRefQueueBounded(); 89 } 90 } 91 92 synchronized void read(long pointer, byte[] buf, int offset, int length ) { 93 try { 94 raf.seek(pointer); 95 raf.readFully(buf, offset, length); 96 } catch(IOException ioe) { 97 throw new MIMEParsingException(ioe); 98 } 99 } 100 101 synchronized long writeTo(long pointer, byte[] data, int offset, int length) { 102 try { 103 raf.seek(pointer); 104 raf.write(data, offset, length); 105 return raf.getFilePointer(); // Update pointer for next write 106 } catch(IOException ioe) { 107 throw new MIMEParsingException(ioe); 108 } 109 } 110 111 void close() { 112 if (LOGGER.isLoggable(Level.FINE)) { 113 LOGGER.log(Level.FINE, "Deleting file = {0}", file.getName()); 114 } 115 refList.remove(this); 116 try { 117 raf.close(); 118 boolean deleted = file.delete(); 119 if (!deleted) { 120 if (LOGGER.isLoggable(Level.INFO)) { 121 LOGGER.log(Level.INFO, "File {0} was not deleted", file.getAbsolutePath()); 122 } 123 } 124 } catch(IOException ioe) { 125 throw new MIMEParsingException(ioe); 126 } 127 } 128 129 void renameTo(File f) { 130 if (LOGGER.isLoggable(Level.FINE)) { 131 LOGGER.log(Level.FINE, "Moving file={0} to={1}", new Object[]{file, f}); 132 } 133 refList.remove(this); 134 try { 135 raf.close(); 136 boolean renamed = file.renameTo(f); 137 if (!renamed) { 138 if (LOGGER.isLoggable(Level.INFO)) { 139 LOGGER.log(Level.INFO, "File {0} was not moved to {1}", new Object[] {file.getAbsolutePath(), f.getAbsolutePath()}); 140 } 141 } 142 } catch(IOException ioe) { 143 throw new MIMEParsingException(ioe); 144 } 145 146 } 147 148 static void drainRefQueueBounded() { 149 WeakDataFile weak; 150 while (( weak = (WeakDataFile) refQueue.poll()) != null ) { 151 if (LOGGER.isLoggable(Level.FINE)) { 152 LOGGER.log(Level.FINE, "Cleaning file = {0} from reference queue.", weak.file); 153 } 154 weak.close(); 155 } 156 } 157 158 private static class CleanupRunnable implements Runnable { 159 @Override 160 public void run() { 161 try { 162 if (LOGGER.isLoggable(Level.FINE)) { 163 LOGGER.log(Level.FINE, "Running cleanup task"); 164 } 165 WeakDataFile weak = (WeakDataFile) refQueue.remove(TIMEOUT); 166 while (weak != null) { 167 if (LOGGER.isLoggable(Level.FINE)) { 168 LOGGER.log(Level.FINE, "Cleaning file = {0} from reference queue.", weak.file); 169 } 170 weak.close(); 171 weak = (WeakDataFile) refQueue.remove(TIMEOUT); 172 } 173 } catch (InterruptedException e) { 174 } 175 } 176 } 177 }