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 }