OSDN Git Service

抜けたthisを補充
[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.FileInputStream;
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.LinkedList;
18 import java.util.concurrent.atomic.AtomicBoolean;
19
20 /**
21  * ロックファイルを用いたVM間ロックオブジェクト。
22  *
23  * <p>大昔のNFSではうまく動かないかも。
24  *
25  * <p>一度でもロックに成功したロックファイルは、
26  * VM終了時に消されてしまうので注意。
27  */
28 public class InterVMLock{
29
30     /** 所持するロックオブジェクト一覧。 */
31     private static final Collection<InterVMLock> OWNEDLOCKSET =
32             Collections.synchronizedCollection(
33                 new LinkedList<InterVMLock>()
34             );
35     private static final AtomicBoolean SHUTDOWNGOING =
36             new AtomicBoolean(false);
37
38     static{
39         Runtime runtime = Runtime.getRuntime();
40         runtime.addShutdownHook(new Thread(){
41             @Override
42             public void run(){
43                 shutdown();
44             }
45         });
46     }
47
48
49     private final File lockFile;
50     private boolean isFileOwner = false;
51     private InputStream stream = null;
52     private final Object thisLock = new Object();
53
54
55     /**
56      * コンストラクタ。
57      * この時点ではまだロックファイルの存在は確認されない。
58      * @param lockFile ロックファイル
59      * @throws NullPointerException 引数がnull
60      */
61     public InterVMLock(File lockFile) throws NullPointerException{
62         if(lockFile == null) throw new NullPointerException();
63         this.lockFile = lockFile;
64         return;
65     }
66
67
68     /**
69      * 所持するロックオブジェクトすべてを解放しロックファイルを削除する。
70      */
71     private static void shutdown(){
72         if( ! SHUTDOWNGOING.compareAndSet(false, true) ) return;
73
74         synchronized(OWNEDLOCKSET){
75             for(InterVMLock lock : OWNEDLOCKSET){
76                 lock.releaseImpl();
77             }
78             OWNEDLOCKSET.clear();
79         }
80         return;
81     }
82
83     /**
84      * シャットダウン処理進行中or完了済みか否か判定する。
85      * @return 進行中or完了済みならtrue
86      */
87     protected static boolean isShutdownGoing(){
88         boolean going = SHUTDOWNGOING.get();
89         return going;
90     }
91
92
93     /**
94      * このオブジェクトがロックファイルの作者であるか判定する。
95      * @return 作者ならtrue
96      */
97     public boolean isFileOwner(){
98         boolean result = this.isFileOwner;
99         return result;
100     }
101
102     /**
103      * ロックファイルがディスク上に存在するか判定する。
104      * @return 存在すればtrue
105      */
106     public boolean isExistsFile(){
107         if(this.lockFile.exists()){
108             return true;
109         }
110         return false;
111     }
112
113     /**
114      * ロック対象のファイルを返す。
115      *
116      * <p>勝手に作ったり消したりしないように。
117      *
118      * @return ロック対象ファイル
119      */
120     public File getLockFile(){
121         return this.lockFile;
122     }
123
124     /**
125      * ロックファイルのオープン中のストリームを返す。
126      * ※ 排他制御目的のリソースなので、
127      * 勝手に読み込んだりクローズしたりしないように。
128      * @return オープン中のストリーム。オープンしてなければnull
129      */
130     protected InputStream getOpenedStream(){
131         InputStream result = null;
132
133         synchronized(this.thisLock){
134             if(this.isFileOwner){
135                 result = this.stream;
136             }
137         }
138
139         return result;
140     }
141
142     /**
143      * ロックファイルの強制削除を試みる。
144      * @return 強制削除に成功すればtrue
145      */
146     public boolean forceRemove(){
147         synchronized(this.thisLock){
148             if(this.isFileOwner) release();
149
150             if( ! isExistsFile() ) return true;
151
152             try{
153                 boolean result = this.lockFile.delete();
154                 if( ! result ) return false;
155             }catch(SecurityException e){
156                 return false;
157             }
158
159             if(isExistsFile()) return false;
160         }
161
162         return true;
163     }
164
165     /**
166      * ロックを試みる。
167      * このメソッドは実行をブロックしない。
168      * @return すでにロック済みもしくはロックに成功すればtrue
169      */
170     public boolean tryLock(){
171         if(isShutdownGoing()) return false;
172
173         synchronized(this.thisLock){
174             if(hasLockedByMe()) return true;
175             if(touchLockFile()) return true;
176         }
177
178         return false;
179     }
180
181     /**
182      * 自身によるロックに成功しているか判定する。
183      * @return 自身によるロック中であればtrue
184      */
185     public boolean hasLockedByMe(){
186         boolean result;
187         synchronized(this.thisLock){
188             if( ! this.isFileOwner ){
189                 result = false;
190             }else if( ! this.lockFile.exists() ){
191                 this.isFileOwner = false;
192                 result = false;
193             }else{
194                 result = true;
195             }
196         }
197         return result;
198     }
199
200     /**
201      * ロックファイルを生成する。{@link #tryLock()}の下請け。
202      * 生成されるロックファイルはVM終了時に削除されるよう登録される。
203      * このメソッド実行中にVM終了が重なると、
204      * ロックファイルが正しく削除されない場合がありうる。
205      * @return 成功すればtrue
206      */
207     protected boolean touchLockFile(){
208         synchronized(this.thisLock){
209             boolean created = false;
210             try{
211                 created = this.lockFile.createNewFile();
212             }catch(IOException e){
213                 assert true;   // IGNORE
214             }catch(SecurityException e){
215                 assert true;   // IGNORE
216             }finally{
217                 if(created){
218                     this.isFileOwner = true;
219                     this.lockFile.deleteOnExit();
220                 }else{
221                     this.isFileOwner = false;
222                 }
223             }
224
225             if( ! created )  return false;
226
227             try{
228                 this.stream = new FileInputStream(this.lockFile);
229             }catch(FileNotFoundException e){
230                 this.isFileOwner = false;
231                 this.stream = null;
232                 try{
233                     this.lockFile.delete();
234                 }catch(SecurityException e2){
235                     assert true; // IGNORE
236                 }
237                 return false;
238             }
239
240             synchronized(OWNEDLOCKSET){
241                 OWNEDLOCKSET.add(this);
242             }
243         }
244
245         return true;
246     }
247
248     /**
249      * ロックを解除する。
250      *
251      * <p>自分が作者であるロックファイルは閉じられ削除される。
252      *
253      * <p>削除に失敗しても無視。
254      *
255      * <p>シャットダウン処理進行中の場合は何もしない。
256      */
257     public void release(){
258         if(isShutdownGoing()) return;
259
260         releaseImpl();
261
262         synchronized(OWNEDLOCKSET){
263             OWNEDLOCKSET.remove(this);
264         }
265
266         return;
267     }
268
269     /**
270      * ロックを解除する。{@link #release()}の下請け。
271      *
272      * <p>自分が作者であるロックファイルは閉じられ削除される。
273      *
274      * <p>削除に失敗しても無視。
275      *
276      * <p>シャットダウン処理進行中か否かは無視される。
277      */
278     protected void releaseImpl(){
279         synchronized(this.thisLock){
280             if( ! this.isFileOwner ) return;
281
282             try{
283                 this.stream.close();
284             }catch(IOException e){
285                 assert true;   // IGNORE
286             }finally{
287                 this.stream = null;
288                 try{
289                     this.lockFile.delete();
290                 }catch(SecurityException e){
291                     assert true;   // IGNORE
292                 }finally{
293                     this.isFileOwner = false;
294                 }
295             }
296         }
297
298         return;
299     }
300
301     // TODO {@link java.nio.channels.FileChannnel}によるロック機構の併用。
302
303 }