OSDN Git Service

Merge commit '2458eff3aea04f67893bc824b5cf896fbb767332'
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / InterVMLock.java
diff --git a/src/main/java/jp/sourceforge/jindolf/InterVMLock.java b/src/main/java/jp/sourceforge/jindolf/InterVMLock.java
new file mode 100644 (file)
index 0000000..b927eec
--- /dev/null
@@ -0,0 +1,236 @@
+/*\r
+ * inter-VM file locking\r
+ *\r
+ * Copyright(c) 2009 olyutorskii\r
+ * $Id: InterVMLock.java 952 2009-12-06 14:29:10Z olyutorskii $\r
+ */\r
+\r
+package jp.sourceforge.jindolf;\r
+\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+/**\r
+ * ロックファイルを用いたVM間ロックオブジェクト。\r
+ * 大昔のNFSではうまく動かないかも。\r
+ * 一度でもロックに成功したロックファイルはVM終了時に消されてしまうので注意。\r
+ */\r
+public class InterVMLock{\r
+\r
+    /** 所持するロックオブジェクト一覧。 */\r
+    private static final Set<InterVMLock> ownedLockSet =\r
+            Collections.synchronizedSet(new HashSet<InterVMLock>());\r
+\r
+    static{\r
+        Runtime runtime = Runtime.getRuntime();\r
+        runtime.addShutdownHook(new Thread(){\r
+            @Override public void run(){ clearOwnedLockSet(); }\r
+        });\r
+    }\r
+\r
+    /**\r
+     * 所持するロックオブジェクト一覧への登録。\r
+     * @param lock 登録するロックオブジェクト\r
+     */\r
+    private static void addOwnedLock(InterVMLock lock){\r
+        synchronized(ownedLockSet){\r
+            if( ! lock.isFileOwner() ) return;\r
+            ownedLockSet.add(lock);\r
+            lock.getLockFile().deleteOnExit();\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 所持するロックオブジェクト一覧からの脱退。\r
+     * @param lock 脱退対象のロックオブジェクト\r
+     */\r
+    private static void removeOwnedLock(InterVMLock lock){\r
+        synchronized(ownedLockSet){\r
+            ownedLockSet.remove(lock);\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 所持するロックオブジェクトすべてを解放しロックファイルを削除する。\r
+     */\r
+    private static void clearOwnedLockSet(){\r
+        synchronized(ownedLockSet){\r
+            for(InterVMLock lock : ownedLockSet){\r
+                if( ! lock.isFileOwner() ) continue;\r
+                lock.release();\r
+                try{\r
+                    lock.getLockFile().delete();\r
+                }catch(SecurityException e){\r
+                    // NOTHING\r
+                }\r
+            }\r
+            ownedLockSet.clear();\r
+        }\r
+        return;\r
+    }\r
+\r
+\r
+    private final File lockFile;\r
+    private boolean isFileOwner = false;\r
+    private FileOutputStream stream = null;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * この時点ではまだロックファイルの存在は確認されない。\r
+     * @param lockFile ロックファイル\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public InterVMLock(File lockFile) throws NullPointerException{\r
+        if(lockFile == null) throw new NullPointerException();\r
+        this.lockFile = lockFile;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ロック対象のファイルを返す。\r
+     * @return ロック対象ファイル\r
+     */\r
+    public File getLockFile(){\r
+        return this.lockFile;\r
+    }\r
+\r
+    /**\r
+     * ロックファイルがディスク上に存在するか判定する。\r
+     * @return 存在すればtrue\r
+     */\r
+    public boolean isExistsFile(){\r
+        if(this.lockFile.exists()){\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * このオブジェクトがロックファイルの作者であるか判定する。\r
+     * @return 作者ならtrue\r
+     */\r
+    public synchronized boolean isFileOwner(){\r
+        return this.isFileOwner;\r
+    }\r
+\r
+    /**\r
+     * ロックファイルのオープン中のストリームを返す。\r
+     * ※ 排他制御目的のリソースなので、\r
+     * 勝手に書き込んだりクローズしたりしないように。\r
+     * @return オープン中のストリーム。オープンしてなければnull\r
+     */\r
+    protected synchronized FileOutputStream getOpenedStream(){\r
+        if(isFileOwner()) return this.stream;\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * ロックファイルの強制削除を試みる。\r
+     * @return 強制削除に成功すればtrue\r
+     */\r
+    public synchronized boolean forceRemove(){\r
+        if(isFileOwner()) release();\r
+\r
+        if( ! isExistsFile() ) return true;\r
+\r
+        try{\r
+            boolean result = this.lockFile.delete();\r
+            if( ! result ) return false;\r
+        }catch(SecurityException e){\r
+            return false;\r
+        }\r
+\r
+        if(isExistsFile()) return false;\r
+\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * ロックファイルを生成する。\r
+     * 生成されるロックファイルはVM終了時に削除されるよう登録される。\r
+     * このメソッド実行中にVM終了が重なると、\r
+     * ロックファイルが正しく削除されない場合がありうる。\r
+     * @return 成功すればtrue\r
+     */\r
+    protected synchronized boolean touchLockFile(){\r
+        boolean result = false;\r
+        try{\r
+            result = this.lockFile.createNewFile();\r
+        }catch(IOException e){\r
+            // NOTHING\r
+        }catch(SecurityException e){\r
+            // NOTHING\r
+        }\r
+        if(result == false){\r
+            return false;\r
+        }\r
+\r
+        try{\r
+            this.isFileOwner = true;\r
+            this.stream = new FileOutputStream(this.lockFile);\r
+        }catch(FileNotFoundException e){\r
+            assert false;\r
+            this.isFileOwner = false;\r
+            this.stream = null;\r
+            try{\r
+                this.lockFile.delete();\r
+            }catch(SecurityException e2){\r
+                // NOTHING\r
+            }\r
+            return false;\r
+        }\r
+\r
+        addOwnedLock(this);\r
+\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * ロックを試みる。\r
+     * このメソッドはブロックしない。\r
+     * @return すでにロック済みもしくはロックに成功すればtrue\r
+     */\r
+    public synchronized boolean tryLock(){\r
+        if( isFileOwner() ) return true;\r
+\r
+        if(isExistsFile()) return false;\r
+        if(touchLockFile() != true) return false;\r
+\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * ロックを解除する。\r
+     * 自分が作者であるロックファイルは閉じられ削除される。\r
+     * 削除に失敗しても無視。\r
+     */\r
+    public synchronized void release(){\r
+        if( ! isFileOwner() ) return;\r
+\r
+        try{\r
+            this.stream.close();\r
+        }catch(IOException e){\r
+            // NOTHING\r
+        }finally{\r
+            this.stream = null;\r
+            try{\r
+                this.lockFile.delete();\r
+            }catch(SecurityException e){\r
+                // NOTHING\r
+            }finally{\r
+                removeOwnedLock(this);\r
+                this.isFileOwner = false;\r
+            }\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+}\r