1 /*
2 * Copyright (C) 2007 u6k.yu1@gmail.com, All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of Clarkware Consulting, Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without prior written permission. For written
18 * permission, please contact clarkware@clarkware.com.
19 *
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
21 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 * CLARKWARE CONSULTING OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
26 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 package jp.gr.java_conf.u6k.filelock;
33
34 import java.io.Closeable;
35 import java.io.File;
36 import java.io.IOException;
37 import java.io.RandomAccessFile;
38 import java.nio.channels.FileChannel;
39 import java.nio.channels.FileLock;
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.TreeMap;
44
45 /**
46 * <p>
47 * 指定したファイルをロックします。ディレクトリを指定した場合、子ファイルを再帰的に検索し、ロックします。ロックに失敗しても例外はスローしません。
48 * </p>
49 *
50 * @version $Id: FileLockUtil.java 27 2008-03-05 14:44:06Z u6k $
51 */
52 public final class FileLockUtil implements Closeable {
53
54 /**
55 * <p>
56 * ロックしたファイルのパスと{@link FileLock}インスタンスのマップ。
57 * </p>
58 */
59 private Map<String, FileLock> lockMap = new TreeMap<String, FileLock>();
60
61 /**
62 * <p>
63 * ファイルとチャネルのマップ。
64 * </p>
65 */
66 private Map<String, FileChannel> channelMap = new TreeMap<String, FileChannel>();
67
68 /**
69 * <p>
70 * ロックに成功したパスのリスト。
71 * </p>
72 */
73 private List<String> lockFileList = new ArrayList<String>();
74
75 /**
76 * <p>
77 * ロックに失敗したパスのリスト。
78 * </p>
79 */
80 private List<String> lockFailFileList = new ArrayList<String>();
81
82 /**
83 * <p>
84 * 指定したファイルをロックし、{@link FileLockUtil}インスタンスを初期化します。ディレクトリを指定した場合、子ファイルを再帰的に検索し、ロックします。ロックに失敗しても例外はスローしません。ロックに成功したパスは{@link #lockFiles()}メソッドで、失敗したパスは{@link #lockFailFiles()}メソッドで取得できます。
85 * </p>
86 *
87 * @param paths
88 * ロックするファイルの配列。
89 * @throws NullPointerException
90 * paths引数がnullの場合。paths配列中にnullが混入していた場合。
91 */
92 public FileLockUtil(String[] paths) {
93 /*
94 * 引数を確認します。
95 */
96 if (paths == null) {
97 throw new NullPointerException("paths");
98 }
99 for (int i = 0; i < paths.length; i++) {
100 if (paths[i] == null) {
101 throw new NullPointerException("paths[" + i + "] == null");
102 }
103 }
104
105 /*
106 * ファイルをロックします。
107 */
108 for (String path : paths) {
109 this.lock(path);
110 }
111 }
112
113 private void lock(String path) {
114 // 正規化したファイルを取得します。
115 File file;
116 try {
117 file = new File(path).getCanonicalFile();
118 } catch (IOException e) {
119 this.lockFailFileList.add(path);
120 return;
121 }
122
123 if (file.isFile()) {
124 // ファイルであればロックします。
125 try {
126 FileChannel fch = new RandomAccessFile(file, "rw").getChannel();
127 this.channelMap.put(file.getAbsolutePath(), fch);
128
129 FileLock l = fch.tryLock();
130 this.lockMap.put(file.getAbsolutePath(), l);
131
132 this.lockFileList.add(file.getAbsolutePath());
133 } catch (IOException e) {
134 this.lockFailFileList.add(file.getAbsolutePath());
135 return;
136 }
137 } else if (file.isDirectory()) {
138 // ディレクトリであれば、再帰的にファイルをロックします。
139 for (File child : file.listFiles()) {
140 this.lock(child.getAbsolutePath());
141 }
142 }
143 }
144
145 /**
146 * <p>
147 * ロックに成功したファイルのパスの配列を返します。
148 * </p>
149 *
150 * @return ロックに成功したファイルのパスの配列。
151 */
152 public String[] lockFiles() {
153 return this.lockFileList.toArray(new String[0]);
154 }
155
156 /**
157 * <p>
158 * ロックに失敗したファイルのパスの配列を返します。
159 * </p>
160 *
161 * @return ロックに失敗したファイルのパスの配列。
162 */
163 public String[] lockFailFiles() {
164 return this.lockFailFileList.toArray(new String[0]);
165 }
166
167 /**
168 * <p>
169 * インスタンスが保持している全てのリソースを開放します。つまり、ロックしている全てのファイルは開放され、{@link #lockFiles()}、{@link #lockFailFiles()}メソッドは空配列を返すようになります。
170 * </p>
171 */
172 public void close() {
173 for (FileChannel fch : this.channelMap.values()) {
174 try {
175 fch.close();
176 } catch (IOException e) {
177 e.printStackTrace();
178 }
179 }
180
181 this.channelMap.clear();
182 this.lockMap.clear();
183 this.lockFileList.clear();
184 this.lockFailFileList.clear();
185 }
186
187 }