OSDN Git Service

Maven3対応。
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / config / InterVMLock.java
1 /*
2  * inter-VM file locking
3  *
4  * License : The MIT License
5  * Copyright(c) 2009 olyutorskii
6  */
7
8 package jp.sfjp.jindolf.config;
9
10 import java.io.File;
11 import java.io.FileNotFoundException;
12 import java.io.FileOutputStream;
13 import java.io.IOException;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.Set;
17
18 /**
19  * ロックファイルを用いたVM間ロックオブジェクト。
20  * 大昔のNFSではうまく動かないかも。
21  * 一度でもロックに成功したロックファイルはVM終了時に消されてしまうので注意。
22  */
23 public class InterVMLock{
24
25     /** 所持するロックオブジェクト一覧。 */
26     private static final Set<InterVMLock> ownedLockSet =
27             Collections.synchronizedSet(new HashSet<InterVMLock>());
28
29     static{
30         Runtime runtime = Runtime.getRuntime();
31         runtime.addShutdownHook(new Thread(){
32             @Override public void run(){ clearOwnedLockSet(); }
33         });
34     }
35
36
37     private final File lockFile;
38     private boolean isFileOwner = false;
39     private FileOutputStream stream = null;
40
41
42     /**
43      * コンストラクタ。
44      * この時点ではまだロックファイルの存在は確認されない。
45      * @param lockFile ロックファイル
46      * @throws NullPointerException 引数がnull
47      */
48     public InterVMLock(File lockFile) throws NullPointerException{
49         if(lockFile == null) throw new NullPointerException();
50         this.lockFile = lockFile;
51         return;
52     }
53
54
55     /**
56      * 所持するロックオブジェクト一覧への登録。
57      * @param lock 登録するロックオブジェクト
58      */
59     private static void addOwnedLock(InterVMLock lock){
60         synchronized(ownedLockSet){
61             if( ! lock.isFileOwner() ) return;
62             ownedLockSet.add(lock);
63             lock.getLockFile().deleteOnExit();
64         }
65         return;
66     }
67
68     /**
69      * 所持するロックオブジェクト一覧からの脱退。
70      * @param lock 脱退対象のロックオブジェクト
71      */
72     private static void removeOwnedLock(InterVMLock lock){
73         synchronized(ownedLockSet){
74             ownedLockSet.remove(lock);
75         }
76         return;
77     }
78
79     /**
80      * 所持するロックオブジェクトすべてを解放しロックファイルを削除する。
81      */
82     private static void clearOwnedLockSet(){
83         synchronized(ownedLockSet){
84             for(InterVMLock lock : ownedLockSet){
85                 if( ! lock.isFileOwner() ) continue;
86                 lock.release();
87                 try{
88                     lock.getLockFile().delete();
89                 }catch(SecurityException e){
90                     // NOTHING
91                 }
92             }
93             ownedLockSet.clear();
94         }
95         return;
96     }
97
98     /**
99      * ロック対象のファイルを返す。
100      * @return ロック対象ファイル
101      */
102     public File getLockFile(){
103         return this.lockFile;
104     }
105
106     /**
107      * ロックファイルがディスク上に存在するか判定する。
108      * @return 存在すればtrue
109      */
110     public boolean isExistsFile(){
111         if(this.lockFile.exists()){
112             return true;
113         }
114         return false;
115     }
116
117     /**
118      * このオブジェクトがロックファイルの作者であるか判定する。
119      * @return 作者ならtrue
120      */
121     public synchronized boolean isFileOwner(){
122         return this.isFileOwner;
123     }
124
125     /**
126      * ロックファイルのオープン中のストリームを返す。
127      * ※ 排他制御目的のリソースなので、
128      * 勝手に書き込んだりクローズしたりしないように。
129      * @return オープン中のストリーム。オープンしてなければnull
130      */
131     protected synchronized FileOutputStream getOpenedStream(){
132         if(isFileOwner()) return this.stream;
133         return null;
134     }
135
136     /**
137      * ロックファイルの強制削除を試みる。
138      * @return 強制削除に成功すればtrue
139      */
140     public synchronized boolean forceRemove(){
141         if(isFileOwner()) release();
142
143         if( ! isExistsFile() ) return true;
144
145         try{
146             boolean result = this.lockFile.delete();
147             if( ! result ) return false;
148         }catch(SecurityException e){
149             return false;
150         }
151
152         if(isExistsFile()) return false;
153
154         return true;
155     }
156
157     /**
158      * ロックファイルを生成する。
159      * 生成されるロックファイルはVM終了時に削除されるよう登録される。
160      * このメソッド実行中にVM終了が重なると、
161      * ロックファイルが正しく削除されない場合がありうる。
162      * @return 成功すればtrue
163      */
164     protected synchronized boolean touchLockFile(){
165         boolean result = false;
166         try{
167             result = this.lockFile.createNewFile();
168         }catch(IOException e){
169             // NOTHING
170         }catch(SecurityException e){
171             // NOTHING
172         }
173         if(result == false){
174             return false;
175         }
176
177         try{
178             this.isFileOwner = true;
179             this.stream = new FileOutputStream(this.lockFile);
180         }catch(FileNotFoundException e){
181             assert false;
182             this.isFileOwner = false;
183             this.stream = null;
184             try{
185                 this.lockFile.delete();
186             }catch(SecurityException e2){
187                 // NOTHING
188             }
189             return false;
190         }
191
192         addOwnedLock(this);
193
194         return true;
195     }
196
197     /**
198      * ロックを試みる。
199      * このメソッドはブロックしない。
200      * @return すでにロック済みもしくはロックに成功すればtrue
201      */
202     public synchronized boolean tryLock(){
203         if( isFileOwner() ) return true;
204
205         if(isExistsFile()) return false;
206         if(touchLockFile() != true) return false;
207
208         return true;
209     }
210
211     /**
212      * ロックを解除する。
213      * 自分が作者であるロックファイルは閉じられ削除される。
214      * 削除に失敗しても無視。
215      */
216     public synchronized void release(){
217         if( ! isFileOwner() ) return;
218
219         try{
220             this.stream.close();
221         }catch(IOException e){
222             // NOTHING
223         }finally{
224             this.stream = null;
225             try{
226                 this.lockFile.delete();
227             }catch(SecurityException e){
228                 // NOTHING
229             }finally{
230                 removeOwnedLock(this);
231                 this.isFileOwner = false;
232             }
233         }
234
235         return;
236     }
237
238 }