マシンのクラスターがあり、それぞれがJavaアプリを実行しています。
これらのJavaアプリは、一意のresource.txt
ファイルに同時にアクセスする必要があります。
temp.txt
がすでに存在していても、Javaでresource.txt
ファイルをresource.txt
にアトミックに名前変更する必要があります。
resource.txt
の削除とtemp.txt
の名前変更は、アトミックではないため機能しません(resource.txt
が存在しない小さなタイムフレームが作成されます)。
そしてそれはクロスプラットフォームでなければなりません...
よろしくお願いします!
Java 1.7+)の場合は、Java.nio.file.Files.move(Path source, Path target, CopyOption... options)
をCopyOptions "REPLACE_EXISTING"および "ATOMIC_MOVE"と共に使用します。
例えば:
Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE);
Linux(およびSolarisと他のUNIXオペレーティングシステム)では、宛先ファイルが存在する場合、JavaのFile.renameTo()メソッドが宛先ファイルを上書きしますが、Windowsではそうではありません。
クロスプラットフォームになるには、resource.txtでファイルロックを使用してから、データを上書きする必要があると思います。
ファイルロックの動作はプラットフォームに依存します。一部のプラットフォームでは、ファイルロックは推奨です。つまり、アプリケーションがファイルロックをチェックしない限り、ファイルへのアクセスが妨げられません。他のプラットフォームでは、ファイルロックは必須です。つまり、ファイルロックにより、アプリケーションがファイルにアクセスできなくなります。
try {
// Get a file channel for the file
File file = new File("filename");
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
// Use the file channel to create a lock on the file.
// This method blocks until it can retrieve the lock.
FileLock lock = channel.lock();
// Try acquiring the lock without blocking. This method returns
// null or throws an exception if the file is already locked.
try {
lock = channel.tryLock();
} catch (OverlappingFileLockException e) {
// File is already locked in this thread or virtual machine
}
// Release the lock
lock.release();
// Close the file
channel.close();
} catch (Exception e) {
}
Linuxではデフォルトで任意ロックが使用されますが、Windowsでは強制されます。多分あなたはOSを検出し、Windows用のいくつかのロックコードでUNIXの下でrenameTo()を使用できますか?
Linuxで特定のファイルに対して強制ロックをオンにする方法もありますが、それはあいまいです。モードビットを適切に設定する必要があります。
Linux、System V(System Vインターフェイス定義(SVID)バージョン3を参照)に従い、グループ実行権限のないファイルのsgidビットにより、必須ロックのファイルをマーク
関連するディスカッションは次のとおりです。 http://bugs.Sun.com/bugdatabase/view_bug.do?bug_id=401759
前述のように here は、Windows OSが古いバージョンのアトミックファイル名の変更さえサポートしていないようです。手動のロックメカニズムまたはある種のトランザクションを使用する必要がある可能性が高いです。そのためには、 Apache commonsトランザクション パッケージを調べてみてください。
これがクロスプラットフォームである必要がある場合、2つのオプションを提案します。
名前変更の目的がオンザフライでresource.txtを置き換えることである場合and関係するすべてのプログラムを制御できますand置換の頻度が高くない場合は、以下をせよ。
ファイルを開く/読み取るには:
ファイルを置き換えるには:
これにより、すべての読者が常に有効なファイルを見つけることができます。
しかし、より簡単なのは、次のように単純にループでオープニングを試すことです。
InputStream inp=null;
StopWatch tmr=new StopWatch(); // made up class, not std Java
IOException err=null;
while(inp==null && tmr.elapsed()<5000) { // or some approp. length of time
try { inp=new FileInputStream("resource.txt"); }
catch(IOException thr) { err=thr; sleep(100); } // or some approp. length of time
}
if(inp==null) {
// handle error here - file did not turn up after required elapsed time
throw new IOException("Could not obtain data from resource.txt file");
}
... carry on
ファイル名を変更する前にファイルにfilechannelロックを設定する(そしてロックを取得したら上書きするファイルを削除する)と、ある程度のトラクションが得られる場合があります。 -r
簡単なリネーム機能で解決します。
呼び出し:
File newPath = new File("...");
newPath = checkName(newPath);
Files.copy(file.toPath(), newPath.toPath(), StandardCopyOption.REPLACE_EXISTING);
CheckName関数は、終了するかどうかを確認します。存在する場合は、2つのブラケット(1)からファイル名の終わりまでの数字を連結します。関数:
private static File checkName(File newPath) {
if (Files.exists(newPath.toPath())) {
String extractRegExSubStr = extractRegExSubStr(newPath.getName(), "\\([0-9]+\\)");
if (extractRegExSubStr != null) {
extractRegExSubStr = extractRegExSubStr.replaceAll("\\(|\\)", "");
int parseInt = Integer.parseInt(extractRegExSubStr);
int parseIntPLus = parseInt + 1;
newPath = new File(newPath.getAbsolutePath().replace("(" + parseInt + ")", "(" + parseIntPLus + ")"));
return checkName(newPath);
} else {
newPath = new File(newPath.getAbsolutePath().replace(".pdf", " (" + 1 + ").pdf"));
return checkName(newPath);
}
}
return newPath;
}
private static String extractRegExSubStr(String row, String patternStr) {
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(row);
if (matcher.find()) {
return matcher.group(0);
}
return null;
}
編集: PDFでのみ機能します。他が必要な場合は、.pdfを置き換えるか、.pdfの拡張パラメーターを作成してください。注:ファイルの角かっこ( ')の間に追加の番号が含まれている場合、ファイル名が混乱する可能性があります。