package org.dynmap.utils; import java.io.File; import java.io.FileNotFoundException; import java.util.HashMap; import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; import org.dynmap.Log; /** * Implements soft-locks for prevent concurrency issues with file updates */ public class FileLockManager { private static Object lock = new Object(); private static HashMap filelocks = new HashMap(); private static final Integer WRITELOCK = new Integer(-1); /** * Get write lock on file - exclusive lock, no other writers or readers * @throws InterruptedException */ public static boolean getWriteLock(File f) { String fn = f.getPath(); synchronized(lock) { boolean got_lock = false; while(!got_lock) { Integer lockcnt = filelocks.get(fn); /* Get lock count */ if(lockcnt != null) { /* If any locks, can't get write lock */ try { lock.wait(); } catch (InterruptedException ix) { Log.severe("getWriteLock(" + fn + ") interrupted"); return false; } } else { filelocks.put(fn, WRITELOCK); got_lock = true; } } } //Log.info("getWriteLock(" + f + ")"); return true; } /** * Release write lock */ public static void releaseWriteLock(File f) { String fn = f.getPath(); synchronized(lock) { Integer lockcnt = filelocks.get(fn); /* Get lock count */ if(lockcnt == null) Log.severe("releaseWriteLock(" + fn + ") on unlocked file"); else if(lockcnt.equals(WRITELOCK)) { filelocks.remove(fn); /* Remove lock */ lock.notifyAll(); /* Wake up folks waiting for locks */ } else Log.severe("releaseWriteLock(" + fn + ") on read-locked file"); } //Log.info("releaseWriteLock(" + f + ")"); } /** * Get read lock on file - multiple readers allowed, blocks writers */ public static boolean getReadLock(File f) { return getReadLock(f, -1); } /** * Get read lock on file - multiple readers allowed, blocks writers - with timeout (msec) */ public static boolean getReadLock(File f, long timeout) { String fn = f.getPath(); synchronized(lock) { boolean got_lock = false; boolean first_wait = true; while(!got_lock) { Integer lockcnt = filelocks.get(fn); /* Get lock count */ if(lockcnt == null) { filelocks.put(fn, Integer.valueOf(1)); /* First lock */ got_lock = true; } else if(!lockcnt.equals(WRITELOCK)) { /* Other read locks */ filelocks.put(fn, Integer.valueOf(lockcnt+1)); got_lock = true; } else { /* Write lock in place */ try { if((timeout > 0) && (!first_wait)) { /* We already waited */ return false; } if(timeout < 0) lock.wait(); else lock.wait(timeout); first_wait = false; } catch (InterruptedException ix) { Log.severe("getReadLock(" + fn + ") interrupted"); return false; } } } } //Log.info("getReadLock(" + f + ")"); return true; } /** * Release read lock */ public static void releaseReadLock(File f) { String fn = f.getPath(); synchronized(lock) { Integer lockcnt = filelocks.get(fn); /* Get lock count */ if(lockcnt == null) Log.severe("releaseReadLock(" + fn + ") on unlocked file"); else if(lockcnt.equals(WRITELOCK)) Log.severe("releaseReadLock(" + fn + ") on write-locked file"); else if(lockcnt > 1) { filelocks.put(fn, Integer.valueOf(lockcnt-1)); } else { filelocks.remove(fn); /* Remove lock */ lock.notifyAll(); /* Wake up folks waiting for locks */ } } //Log.info("releaseReadLock(" + f + ")"); } private static final int MAX_WRITE_RETRIES = 6; /** * Wrapper for IOImage.write - implements retries for busy files */ public static void imageIOWrite(BufferedImage img, String type, File fname) throws IOException { int retrycnt = 0; boolean done = false; while(!done) { try { ImageIO.write(img, type, fname); done = true; } catch (IOException fnfx) { if(retrycnt < MAX_WRITE_RETRIES) { Log.info("Image file " + fname.getPath() + " - unable to write - retry #" + retrycnt); try { Thread.sleep(50 << retrycnt); } catch (InterruptedException ix) { throw fnfx; } retrycnt++; } else { Log.info("Image file " + fname.getPath() + " - unable to write - failed"); throw fnfx; } } } } }