OSDN Git Service

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