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