OSDN Git Service

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