2011-07-20 10 views
13

File.mkdirを使用している間、友人は失敗時に例外をスローしないことに気付きました!ありがたいことに、FindBugsはこれを指摘しましたが、戻り値を少なくともチェックするコードがありましたが、まだ意味のある情報を得る方法はありませんなぜ呼び出しが失敗しますか?Javaファイルオブジェクト(mkdir、rename、delete)への呼び出しに意味のあるメッセージを取得する方法

これらのファイルメソッドへの呼び出しが失敗する理由を確認するにはどうすればよいですか?これを処理する良い代替ライブラリやライブラリがありますか?

私はここでSOとGoogleでいくつかの検索を行い、このトピックに関する驚くほど小さな情報を発見しました。

[更新]私はVFSを試してみましたが、その例外はもはや有用な情報を持っていません。たとえば、最近削除されたディレクトリを移動しようとすると、Could not rename file "D:\path\to\fileA" to "file:///D:/path/do/fileB". fileAは存在しなくなりました。

JDK 1.7が

+0

わかりません。しかし、ディレクトリを作成する過程でどのような例外がスローされるのか、なぜそれが失敗するのかを知るためには、ソースコードをチェックすることをお勧めします。 – Moonbeam

+0

@Moonbean:重要な点は、ディレクトリに既に存在する場合のように例外がスローされないことです。 –

答えて

8

ネイティブメソッドを呼び出して、適切なエラーコードを取得できます。たとえば、c関数mkdirには、EEXISTやENOSPCのようなエラーコードがあります。 JNAを使用すると、これらのネイティブ関数に簡単にアクセスできます。 * nixとwindowsをサポートしている場合は、このコードの2つのバージョンを作成する必要があります。あなたがこれを行うことができますLinux上JNAます。mkdirの例えば

import java.io.IOException; 

import com.sun.jna.LastErrorException; 
import com.sun.jna.Native; 

public class FileUtils { 

    private static final int EACCES = 13; 
    private static final int EEXIST = 17; 
    private static final int EMLINK = 31; 
    private static final int EROFS = 30; 
    private static final int ENOSPC = 28; 
    private static final int ENAMETOOLONG = 63; 

    static void mkdir(String path) throws IOException { 

    try { 
     NativeLinkFileUtils.mkdir(path); 

    } catch (LastErrorException e) { 
     int errno = e.getErrorCode(); 
     if (errno == EACCES) 
     throw new IOException(
      "Write permission is denied for the parent directory in which the new directory is to be added."); 
     if (errno == EEXIST) 
     throw new IOException("A file named " + path + " already exists."); 
     if (errno == EMLINK) 
     throw new IOException(
      "The parent directory has too many links (entries). Well-designed file systems never report this error, because they permit more links than your disk could possibly hold. However, you must still take account of the possibility of this error, as it could result from network access to a file system on another machine."); 
     if (errno == ENOSPC) 
     throw new IOException(
      "The file system doesn't have enough room to create the new directory."); 
     if (errno == EROFS) 
     throw new IOException(
      "The parent directory of the directory being created is on a read-only file system and cannot be modified."); 
     if (errno == EACCES) 
     throw new IOException(
      "The process does not have search permission for a directory component of the file name."); 
     if (errno == ENAMETOOLONG) 
     throw new IOException(
      "This error is used when either the total length of a file name is greater than PATH_MAX, or when an individual file name component has a length greater than NAME_MAX. See section 31.6 Limits on File System Capacity."); 
     else 
     throw new IOException("unknown error:" + errno); 
    } 




    } 
} 

class NativeLinkFileUtils { 
    static { 
    try { 
     Native.register("c"); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    } 

    static native int mkdir(String dir) throws LastErrorException; 

} 
+0

コード例をありがとう、このアプリはWindows中心ですが、それは簡単な翻訳でなければなりません。私の唯一の傑出した問題は、これがWindowsのUNCパスとどのようにやり取りするかということです私はCからのウィンドウベースのネットワークパスでは一度も働いたことはありません。私はいつも自分自身を見ることができると思いますが、あなたはすでにここで返信できることを知っています:) –

+0

私の窓と知識はかなり限られていますが、スタックオーバーフローに関する質問。 – sbridges

5

あなたはこのようないくつかのコンテンツとユーティリティクラスを作ることができているので、[更新]ビジネス要件は、1.6ソリューションのみJDKに私を制限:もちろん

public int mkdir(File dirToCreate) throws IOException 
{ 
    if (dirToCreate.exists()) 
     throw new IOException("Folder already exists"); 

    if (!dirToCreate.getParent().canWrite()) 
     throw new IOException("No write access to create the folder"); 

    return dirToCreate.mkdir(); 
} 


public int rename(File from, File to) throws IOException, FileNotFoundException 
{ 
    if (from.equals(to)) 
     throw new IllegalArgumentException("Files are equal"); 

    if (!from.exists()) 
     throw new FileNotFoundException(from.getAbsolutePath() + " is not found"); 

    if (!to.getParent().exists()) 
     throw new IllegalAccessException("Parent of the destination doesn't exist"); 

    if (!to.getParent().canWrite()) 
     throw new IllegalAccessException("No write access to move the file/folder"); 

    return from.renameTo(to); 
} 

これは完全ではありませんが、あなたはこの考えを理解することができます。

+3

これは可能ですが、「正しく」取得するのは難しいかもしれません。基礎となるシステムがちょうど「知る」べきであるかどうかをテストする可能性はたくさんあります。たとえば、ネットワークエラーのためにmkdir omネットワークパスが失敗したことを検出します。 –

+2

そして、ディスクが一杯になったらどうなりますか? – Arjan

+4

このアプローチは、競合状態にも苦しんでいます。 –

1

代わりにjakarta VFSを使用できます。 FileObject.createFolder()は、エラーコードを保持するFileSystemExceptionをスローします。これは、提供されるロジックを実装する代わりに、@Martijn Courteaux

+0

'FileObject'インターフェースには' createFolder'メソッドがありません。 – Arjan

+0

@Arjan:http://commons.apache.org/vfs/apidocs/org/apache/commons/vfs/FileObject.html#createFolder() –

+0

@Andrew:私は訂正しました。私はhttp:// downloadを見つけました。 oracle.com/javase/6/docs/api/javax/tools/FileObject.html – Arjan

4

JDK7のnew file APIを使用してください。 OS統合の方がはるかに優れており、より詳細なフィードバックを提供します。たとえば、docs for moving/renamingを参照してください。

+0

ehhh、それは素晴らしいことですが、要件を除いては、JDK 1.6のみを使用することです。 –

4

あなたはコマンドを実行すると、意味のあるメッセージが含まれているerr出力をキャプチャすることができます。ここで

が、これは仕事ができる方法を示しています(apache commons-execを使用しています)いくつかの最小限の実行可能なコードは次のとおりです。

import org.apache.commons.exec.*; 

public static String getErrorMessage(String command) { 
    CommandLine cmdLine = CommandLine.parse(command); 
    DefaultExecutor executor = new DefaultExecutor(); 
    ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    ByteArrayOutputStream err = new ByteArrayOutputStream(); 
    executor.setStreamHandler(new PumpStreamHandler(out, err)); 
    try { 
     executor.execute(cmdLine); 
    } catch (Exception e) { 
     return err.toString().trim(); 
    } 
    return null; // no error occurred 
} 

ここでファイル操作エラーの様々な条件を示すこのコードのテストです:

public static void main(String[] args) throws Exception { 
    System.out.println(getErrorMessage("cp fake.file x")); 
    System.out.println(getErrorMessage("cp /tmp /tmp")); 
    System.out.println(getErrorMessage("mkdir /Volumes")); 
    System.out.println(getErrorMessage("mv /tmp /")); 
    System.out.println(getErrorMessage("mv fake.file /tmp")); 
} 

出力(mac osxで実行):

cp: fake.file: No such file or directory 
cp: /tmp is a directory (not copied). 
mkdir: /Volumes: File exists 
mv: /tmp and /tmp are identical 
mv: rename fake.file to /tmp/fake.file: No such file or directory 

例えば、特定のIOExceptionsに正規表現のマッチングやcontainsを使用して主要なパラメータとマップメッセージのためにそれを解析し、それらを投げることができ、メッセージを得た、ということにIOExceptionをスローする方法では、上記の方法をラップ:

if (message.endsWith("No such file or directory")) 
    throw new FileNotFoundException(); // Use IOExceptions if you can 
if (message.endsWith("are identical")) 
    throw new IdenticalFileException(); // Create your own Exceptions that extend IOException 

あなたの場合これを抽象化して複数のOSのフレーバで使用するには、プラットフォームごとにコードを実装する必要があります(Windowsおよび* nixは、任意のファイル操作/結果に対して異なるシェルコマンド/エラーメッセージを使用します)。

この回答に賞金が授与された場合は、ファイル操作用のスタイリッシュなenumを含む完全な整然としたバージョンの作業コードを投稿します。

+0

これは完全に有効な解決策であることに同意します。それはある種のエレガンスが欠けているようで、入力を害さないためには細心の注意を払わなければならないでしょう。 –

+0

醜い実装がエレガントなファサード(マイクロソフトのソフトウェアのようなビット;-)の後ろに隠れている限り、それは受け入れられます – Bohemian

関連する問題