2010-02-16 2 views
9

JRE 6, su Windows XP.Java: incoerenze File.exists() quando si imposta "user.dir"

L'installazione di due oggetti File con diversi costruttori porta a risultati incoerenti nel metodo File.exists().

responsabilità: il codice qui sotto rappresenta un estratto, non il codice vero e proprio. Non credo che questo sia un problema File.separator. Prima ho chiesto di ottenere reazioni tempestive, nel caso in cui mi fossi perso un problema ben compreso. Sembra che la reimpostazione della proprietà di sistema user.dir sia una delle cause di questo problema. Il codice sotto è ora riproducibile e utilizzabile così com'è. Puoi copiare/incollare la classe Java e provarla, dovrebbe comportarsi in modo coerente con ciò che ho elencato come risultato.

Setup:

Creare l'architettura della cartella C:\toto\tmp\sub.

Avviare la seguente classe da qualsiasi cartella che non contenga un'architettura di sottocartella tmp/sub.

Codice:

public class TestFileExists { 

    public static void main(String[] args) { 

     System.setProperty("user.dir", "C:\\toto\\"); 

     File root = new File("tmp"); 

     File sub_a = new File(root, "sub"); 

     File sub_b = new File(root.getAbsolutePath()+"/sub"); 

     System.out.println("sub_a path ? "+sub_a.getAbsolutePath()); 
     System.out.println("sub_a exists ? "+sub_a.exists()); 
     System.out.println("sub_b path ? "+sub_b.getAbsolutePath()); 
     System.out.println("sub_b exists ? "+sub_b.exists()); 
     System.out.println("Path equals ? "+ (sub_a.getAbsolutePath().equals(sub_b.getAbsolutePath()))); 
     System.out.println("Obj equals ? "+ (sub_a.equals(sub_b))); 

    } 

} 

Risultato:

sub_a path ? C:\toto\tmp\sub 
sub_a exists ? false 
sub_b path ? C:\toto\tmp\sub 
sub_b exists ? true 
Path equals ? true 
Obj equals ? false 

Non capisco la linea sub_a exists ? false, e il risultato non è coerente da macchina a macchina, né con la percorso iniziale root il risultato ora è coerente con da macchina a macchina.

Ora, se si rieseguire la classe chiamando Java dalla riga di comando, da una cartella che contiene un'architettura sottocartella tmp/sub (come se si chiama da D:\, avendo D:\tmp\sub), si otterrà l'atteso:

sub_a path ? C:\toto\tmp\sub 
sub_a exists ? true 
sub_b path ? C:\toto\tmp\sub 
sub_b exists ? true 
Path equals ? true 
Obj equals ? false 

Ma l'esistenza di sub_a è chiaramente un falso positivo, perché controlla l'esistenza di una cartella diversa da quella descritta dal getAbsolutePath().

Così ho il forte sospetto che File.exists() dipende l'attuale percorso di esecuzione Java, e che il file esistenza non è coerente con il percorso assoluto, e exists() utilizza un altro percorso rispetto alla proprietà di sistema "user.dir" per controllare il file system.

Qualche idea di questo problema?

+1

Wild guess: alcuni attributi avvitati su una directory intermedia che viene attraversata con il percorso assoluto. –

+0

Su Windows XP con JDK 1.6.0_16 e Java 1.6.0_18, ottengo vero, vero. vero falso. Detto questo, la mia directory non ha spazi, caratteri speciali o autorizzazioni negate su di essa. – Powerlord

+1

Il downgoter può spiegare perché? – glmxndr

risposta

9

L'impostazione user.dir non è supportata. Dovrebbe essere considerato una proprietà di sola lettura.

Per esempio la valutazione della Bug 4117557 nella parata Sun Bug contiene questo testo:

"user.dir", che viene inizializzato durante l'avvio JVM, dovrebbero essere utilizzati come una proprietà del sistema informativo /sola lettura, prova a personalizzarlo tramite la riga di comando -Duser.dir = xyz finirà con il comportamento dipendente dall'implementazione/non specificato.

Anche se questo testo è di circa impostandola sulla riga di comando, l'impostazione tramite setProperty() è più probabile altrettanto indefinito.

Quando è possibile riprodurre manualmente il problema senza impostando user.dir, è stato riscontrato un problema reale.

+0

Perfetto, grazie mille. – glmxndr

+0

Il motivo per cui questo non funziona non è dovuto al fatto che l'impostazione di user.dir non è supportata. È perché user.dir viene ignorato quando si utilizzano percorsi relativi per i file, mentre File.getAbsolutePath() lo prende in considerazione. Ciò significa che lo stesso file può fare riferimento a due posizioni, a seconda che lo si converta in file assoluto o meno. Soluzione: evitare l'uso di percorsi relativi. –

+0

@Roman: Ci dispiace, ma ti sbagli. Questo è solo un effetto del fatto che il sistema presuppone che 'user.dir' non venga mai modificato dall'utente. 'getAbsolutePath()' è documentato per "risolto in modo dipendente dal sistema" rispetto alla directory utente corrente. Quindi 'getAbsolutePath()' dovrebbe * sempre * restituire esattamente lo stesso percorso a cui si accederà se si è appena usato il percorso relativo direttamente per accedere a un file. Questo è l'intero punto di quel metodo! –

0

Su questa linea:

File sub_b = new File(root.getAbsolutePath()+"/sub"); 

Si deve usare la costante File.separator (dipendente dal sistema sottostante) anziché una barra hard-coded.

+1

No, la prima cosa che fa il costruttore File è di normalizzare il modulo, quindi non dovrebbe essere d'aiuto. –

+0

No, ho fatto un errore nel copiare i risultati. In realtà è un sub_a che viene rilevato come non esistente. – glmxndr

0

Penso che sia il separatore di directory, dal momento che Windows di solito usa la barra rovesciata "\" mentre Linux usa la barra normale "/".

Provare a cambiare la linea in:

File sub_b = new File(root.getAbsolutePath() + System.getProperty("file.separator") + "sub"); 
+0

No, ho commesso un errore nel copiare i risultati. In realtà è un sub_a che viene rilevato come non esistente. Ancora una volta mi dispiace. – glmxndr

0

Questo può essere un problema di separazione di file. Su Windows, il separatore di file standard è il backslash (\). Quando crei sub_b, crei un nome percorso (come String) che contiene sia barre che barre rovesciate. Il sistema potrebbe essere un po 'confuso su questo.

+0

No, ho commesso un errore nel copiare i risultati. In realtà è un sub_a che viene rilevato come non esistente. Scusate. Domanda modificata. Ancora una volta mi dispiace. – glmxndr

0

Questi risultati devono essere deterministici tra le macchine. Lasciatemi scomporre per riga:

System.out.println("sub_a exists ? "+sub_a.exists()); 

Qui, stai chiedendo se questo file esiste realmente nel filesystem. Questo dovrebbe sempre restituire lo stesso, supponendo che il file esista.

System.out.println("sub_b exists ? "+sub_b.exists()); 

Stessa cosa. Stai verificando se questo file esiste realmente.

System.out.println("Path equals ? "+ (sub_a.getAbsolutePath().equals(sub_b.getAbsolutePath()))); 

Qui si sta vedendo se il AbsolutePath è lo stesso.

System.out.println("Obj equals ? "+ (sub_a.equals(sub_b))); 

E qui, si sta facendo un confronto con oggetto .equals(), che sotto il cofano sarà chiamare la classe FileSystem per fare un confronto() del percorso due dell'oggetto oggetti, non la loro AbsolutePath.

Probabilmente il separatore di file è sbagliato. Provare a sostituire File.separator nella costruzione di sub_b, in questo modo:

File sub_b = new File(root.getAbsolutePath()+File.separator+"sub"); 
1

File lattina rappresenta un astratto percorso . La creazione di une uno dal percorso assoluto di tmp non sarà uguale agli oggetti File sebbene i loro percorsi assoluti siano uguali.

Non sono esattamente sicuro di uno scenario in cui sub_a non esisterà ma sub_b ma dubito che sia un problema di separatore. Ho il sospetto che sia qualcosa a che fare con questa affermazione dal javadoc for File(File,String):

ogni stringa percorso viene convertito in un percorso astratto e il bambino percorso astratto si risolve contro il genitore.

Non so una situazione su file system basati su Unix, dove, dall'interno /full/path/to, ./tmp/sub esisterà ma /full/path/to/tmp non lo faranno.

Se il problema è coerente tra i sistemi, può essere compreso più chiaramente scaricando più di ogni stato dell'oggetto File invece di stampare solo i confronti.

+0

Sono d'accordo con te sulla traccia non separatore. Sembra che accada quando viene impostata la proprietà 'user.dir'. Aggiornerò la domanda non appena avrò una comprensione più approfondita sul contesto dell'esecuzione. – glmxndr

2

Aggiungere le seguenti righe al vostro test:

System.out.println("sub_a = " + sub_a); 
System.out.println("sub_b = " + sub_b); 

Conclusioni:

1) sub_a è un percorso relativo e sub_b è assoluto.

2) exists() non utilizza il percorso assoluto

3) impostando la variabile ambientale user.dir non cambia la directory utente corrente, utilizzato da exists()

4) getAbsolutePath utilizza la variabile user.dir invece del reale directory utente corrente! Almeno per Windows (vedi Win32FileSystem.getuserPath). Questo è il problema! (Bug?)

+0

Assolutamente. Sembra però che non sia un bug, ma un abuso del user.dir, che non è stato concepito per essere modificabile durante l'esecuzione. – glmxndr