|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
[JAVA-ECLIPSE-JAR] Un'odissea senza fine. Problemi con JAR non funzionanti
Salve gente,
Ho sviluppato un'applicazione in Java piuttosto corposa e vorrei fare un file jar per motivi di praticità. L'applicazione ha diversi package, e nella root del progetto ho un file chiamato config.xml che mantiene alcune proprietà, e impostazioni. Se avvio l'applicazione da riga di comando senza jar file, tutto procede liscio. Se creo un JAR file con Eclipse, non appena lo avvio mi dice che non riesce a trovare questo file config.xml, anche se in realtà c'è, ed è nella root dell'archivio. Sono mesi che provo a farlo funzionare senza esito positivo, ma ora ne ho davvero bisogno. Qualcuno può darmi una mano? Grazie |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Se il file è nel jar del programma (o nella root di un qualsiasi altro jar o zip incluso nel classpath) allora il suo percorso è:
URL config = getClass().getResource("/config.xml"); Se il file non è all'interno di un jar allora il suo percorso assoluto è relativo alla proprietà di sistema user.dir - che varia secondo il percorso della directory di esecuzione del programma. Se il file non è all'interno del jar ma è comunque in una posizione determinabile a partire da uno dei jar parte del tuo programma puoi determinarne la posizione assoluta rispetto alla posizione di quel jar - che acquisici trasformando il percorso assoluto di uno dei file .class contenuti in quel jar.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
Ciao e grazie per l'aiuto.
Allora, nel frattempo sono stato contattato in privato anche da ^TiGeRShArK^ che al momento non può postare. Siamo arrivati a questa conclusione, a partire da quanto scritto qui. Magari nel frattempo ho capito male io, ma vediamo come ho fatto io fin'ora: Codice:
private FileInputStream fis = null; // variabile di istanza [...] // all'interno di un metodo di inizializzazione properties = new Properties(); try { fis = new FileInputStream("config.xml"); } catch (FileNotFoundException e) { JOptionPane.showMessageDialog(getThis(), "File di configurazione mancante.", "Errore", JOptionPane.ERROR_MESSAGE); System.exit(-1); } try { properties.loadFromXML(fis); } catch (InvalidPropertiesFormatException e) { JOptionPane.showMessageDialog(getThis(), "File di configurazione corrotto.", "Errore", JOptionPane.ERROR_MESSAGE); System.exit(-1); } catch (IOException e) { JOptionPane.showMessageDialog(getThis(), "Errore di I/O.", "Errore", JOptionPane.ERROR_MESSAGE); System.exit(-1); } Codice:
private InputStream fis = null; // variabile di istanza [...] properties = new Properties(); fis = getClass().getResourceAsStream ("/config.xml"); try { properties.loadFromXML(fis); } catch (InvalidPropertiesFormatException e) { JOptionPane.showMessageDialog(getThis(), "File di configurazione corrotto.", "Errore", JOptionPane.ERROR_MESSAGE); System.exit(-1); } catch (IOException e) { JOptionPane.showMessageDialog(getThis(), "Errore di I/O.", "Errore", JOptionPane.ERROR_MESSAGE); System.exit(-1); } Ora, dal tuo post mi sembra di capire che invece dovrei utilizzare la classe URL. Però poi non posso usare il metodo loadFromXML(fis) su properties. Allora la questione è questa: dovrei cambiare radicalmente il modo di caricare ed usare il file, ma non sapendo esattamente come, ho un attimo di disorientamento in merito. |
![]() |
![]() |
![]() |
#4 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
La creazione del file xml al di fuori del jar non avviene ad opera del codice che hai incollato: getResource non genera file, nè direttamente nè indirettamente.
Se hai un file config.xml nella radice del jar del tuo programma quel getClass().getResourceAsStream("/config.xml") lo becca al 100%. Non c'è differenza tra getResource e getResourceAsStream (il secondo è uno shortcut per getResource("abc").openStream()): entrambi danno come risultato l'accesso ad una risorsa inclusa nel classpath.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#5 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
No ma infatti lo becca, solo che poi le cose non funzionano come dovrebbero.
|
![]() |
![]() |
![]() |
#6 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
La trama si infittisce.
L'altro config.xml, quello che appare nella cartella in cui si trova il jar che contiene il "vero" config.xml, dove lo crei?
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#7 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
Allora, c'è un solo config.xml.
La situazione è questa. Nella root del progetto ho il file config.xml che semplicemente contiene 2 proprietà, due campi che devono essere modificabili e leggibili, a seconda di quello che fa l'utente. All'avvio, questo file, config.xml viene caricato e vengono letti questi parametri. Fino a che non ho il bisogno di fare un file .jar, il programma si avvia e funziona tutto correttamente, ma dal momento che creo il jar il file di configurazione non viene letto. Ora, apportando le modifiche che mi hai detto tu, e ^TiGeRShArK^ in privato, e sono modifiche che propongono la stessa soluzione, quando vado ad avviare il programma da jar, mi compare il form di login del programma, poi, nella stessa dir dove si trova il jar, viene creato un file config.xml che è la copia esatta di quello che si trova nel jar, e una volta dato l'OK nel form di login, invece di comparire la finestra principale del programma, quest'ultimo viene terminato senza dare altro segno. Comunque, se tanto i files non possono essere modificati quando sono all'interno del jar file, allora è un problema. Io ho anche un altro file che viene modificato per memorizzare/leggere delle proprietà, e anch'esso verrebbe a trovarsi nel jar file. A questo punto non so se conviene creare il .jar o no, anche se "esteticamente" mi sembrerebbe più elegante. |
![]() |
![]() |
![]() |
#8 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Con ogni probabilità il secondo file config.xml lo crei quando vai ad aggiornare le proprietà contenute nel file config.xml.
E' un po' curiosa come faccenda perchè in genere uno sa con certezza quali file crea la sua applicazione e dove li crea ![]() Nota che se un programma non funziona quando è impacchettato in un Jar allora il suo funzionamento in versione non-jar è meramente accidentale nel senso che dipende dal valore di una variabile di sistema - che in quanto variabile varia da "evviva" a "braghe di tela". Ciò premesso, puoi scrivere nel jar eliminando il vecchio e sostituendolo con un jar nuovo - in cui il vecchio file config.xml risulti sovrascritto. Puoi scrivere il tuo file config.xml in una delle cartelle predefinite del sistema operativo dedicata ai file delle applicazioni: tieni quello che hai nel jar come base e lo carichi solo se manchi quell'altro. Puoi scrivere il tuo file config.xml nella cartella in cui si trova il jar. Personalmente uso l'ultima opzione, con questo strumento: Codice:
package it.tukano.datacoat; import java.net.*; import java.io.*; /** Determina il percorso di base del programma. Il percorso di base è la cartella in cui si trova il file .jar che contiene questa classe o la radice del package a cui appartiene questa classe. */ class PathFinder { private static final PathFinder instance = new PathFinder(); /** Restituisce un'istanza condivisa di PathFinder. */ public static final PathFinder getInstance() { return instance; } private final String BASE_PATH; /** Costruisce un PathFinder inizializzando il percorso di base */ public PathFinder() { BASE_PATH = computeBasePath(); } /** Restituisce il percorso di base del programma. */ public String getBasePath() { return BASE_PATH; } /** Restituisce un file il cui percorso è relativo alla cartella di base del programma. */ public File getRelativeFile(String relativeName) { return new File(BASE_PATH + File.separator + relativeName); } /** Computa il percorso della cartella di base del programma. */ private String computeBasePath() { Class<?> thisClass = PathFinder.class; String name = thisClass.getSimpleName() + ".class"; String classPath = thisClass.getResource(name).toString(); String fullName = thisClass.getCanonicalName().replace('.', '/'); classPath = classPath.substring(0, classPath.indexOf(fullName)); try { classPath = URLDecoder.decode(classPath, "utf-8"); } catch(UnsupportedEncodingException ex) { throw new InternalError("utf-8 unsupported?"); //utf-8 deve essere supportato } if(classPath.startsWith("jar")) { return parseJarPath(classPath); } else if(classPath.startsWith("file")) { return parseFilePath(classPath); } else { return null; } } private String parseFilePath(String path) { path = path.substring("file:/".length(), path.length()); if(path.endsWith("/")) { path = path.substring(0, path.length() - 1); } path = path.replace('/', File.separatorChar); return path; } private String parseJarPath(String path) { path = path.substring("jar:file:/".length(), path.length()); int exIndex = path.lastIndexOf('!'); path = path.substring(0, exIndex); int slashIndex = path.lastIndexOf('/'); path = path.substring(0, slashIndex); path = path.replace('/', File.separatorChar); return path; } } File file = PathFinder.getInstance().getRelativeFile("config.xml"); e "file" risulterà essere un file nella cartella in cui si trova il jar, a prescindere da qualsiasi variabile d'ambiente. L'approccio standard è tuttavia quello di usare cartelle di sistema, tipo "application data" o quel che è.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#9 | ||||||
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
Quote:
Quote:
Quote:
![]() Quote:
Quote:
Quote:
Per ora ti ringrazio !!!! |
||||||
![]() |
![]() |
![]() |
#10 | |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
Quote:
sto provando finalmente la tua classe. Purtroppo però non mi funziona. Il file config.xml è nella root del progetto: Codice:
. |-- Images |-- bin |-- config.xml |-- lib `-- src Codice:
File file = PathFinder.getInstance().getRelativeFile("config.xml"); FileInputStream fis = new FileInputStream(file); Prima facevo semplicemente Codice:
FileInputStream fis = new FileInputStream("config.xml"); Adesso proprio non trova il file se faccio come dici tu. Perché, secondo te? Devo forse modificare la classe PathFinder? EDIT: Ho scoperto che il basepath che mi trova è: home/fbcyborg/workspace/ProjectName/bin Quindi è per questo che non trova il file. Ultima modifica di fbcyborg : 16-02-2010 alle 19:32. |
|
![]() |
![]() |
![]() |
#11 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Questo
FileInputStream fis = new FileInputStream("config.xml"); funziona se e solo se il valore di "user.dir" è la cartella in cui è contenuto "config.xml". PathFinder risolve il percorso rispetto alla radice del package (per classi "sfuse") o alla cartella che contiene il jar. Se le classi sono in "bin" allora il PathFinder.findeccetera("config.xml") restituisce y:\qualcosa\bin\config.xml Il tuo config sembra invece essere nella cartella superiore.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#12 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
Esattamente! Quindi come modifico la classe PathFinder, e in particolar modo il metodo computeBasePath() affinché mi restituisca la dir al livello superiore?
|
![]() |
![]() |
![]() |
#13 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
La cartella superiore sarebbe new File(pathFinder.getBasePath()).getParentFile() ma ha poco senso che il file di configurazione stia nella cartella superiore, di solito è in una inferiore:
programma /librerie /risorse /configurazione Comunque tutto dipende da dove decidi tu di mettere il file. Tieni conto che la struttura dei file che eclipse usa è ad uso e consumo di eclipse. Quando distribuirai il programma non darai all'utente un progetto eclipse. Gli darai un jar
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#14 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
OK,
mi sono creato una directory config e vi ho piazzato dentro due files, fra cui il mio config.xml, ma quando faccio Codice:
File file = PathFinder.getInstance().getRelativeFile("config.xml"); E questo perché secondo questa classe la root del programma è MyProject/bin |
![]() |
![]() |
![]() |
#15 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
![]() Il problema si può risolvere con uno qualsiasi degli strumenti che hai già tentato, dal new File("config.xml") al getResourceAsStream("/config.xml") passando per PathFinder eccetera. Devi prima di tutto stabilire dove si trova il file config.xml. "Dove si trova" signifca percorso assoluto O percorso relativo. Nel nostro caso è relativo, quindi devi stabilire "relativo a che cosa" Posto che non puoi usare la struttura di cartelle del progetto eclipse perchè quelle valgono solo in eclipse, la posizione del tuo file config.xml è relativa a che cosa?
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#16 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
Ok ok..
Allora ho capito quello che vuoi dire. A me serve usare un percorso relativo, o meglio voglio caricare un certo file tramite l'utilizzo di un path relativo. Ti posto (in parte) l'albero del progetto: Codice:
. |-- Images |-- bin | `-- gui | `-- Login.class |-- config | `-- config.xml |-- lib `-- src `-- gui `-- Login.java Ora, finché il file config.xml si trovava nella root del progetto (ovvero allo stesso livello di Images, bin, lib e src) mi era sufficiente fare Codice:
FileInputStream fis = new FileInputStream("config.xml"); Ora, utilizzando la classe PathFinder, la root del progetto che viene restituita è bin/. Spero che le indicazioni che ti ho dato ora, siano sufficienti per capire come meglio risolvere la questione. Grazie davvero! EDIT: OK, dovrei aver risolto semplicemente con: Codice:
fis = new FileInputStream("config"+SLASH+"config.xml"); Ora provo con il file jar... ti faccio sapere. Ultima modifica di fbcyborg : 16-02-2010 alle 22:57. |
![]() |
![]() |
![]() |
#17 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
Niente da fare
![]() Ora sembra che nel jar Eclipse non voglia impacchettare la directory config/ e giustamente il programma si lamenta che non trova il file config.xml in essa contenuto. Non ci sto più capendo nulla. Se faccio Export Runnable Jar File, non riesco a mettere questa directory nel jar. Se faccio Export Jar File, posso mettercela, ma non funziona lo stesso (non mi mette molte delle librerie esterne che ho usato).. ![]() Che stress! |
![]() |
![]() |
![]() |
#18 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Se funziona come netbeans, per fargli impacchettare anche la cartella config/config.xml devi metterla tra i sorgenti (sotto src).
Codice:
. |-- Images |-- bin | `-- gui | `-- Login.class |-- lib `-- src -- config -- config.xml `-- gui `-- Login.java getClass().getResource("/config/config.xml"); A patto che eclipse te lo impacchetti.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#19 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
OK!
Grazie, ora in effetti funziona! L'unico problema è quando faccio il jar, perché non mi trova il file di configurazione, ma questa volta per un motivo chiaro: il path nel quale va a cercarlo è: null/config/config.xml Forse c'è qualcosa nella classe PathFinder che va aggiustato. La classe PathFinder l'ho modificata, ma di poco. Per esempio ho modificato una riga nel metodo computeBasePath(): Codice:
} else if(classPath.startsWith("file")) { return File.separator + parseFilePath(classPath); } else { Inoltre la classe l'ho messa pubblica, sennò mi veniva vista solo nel package in cui l'avevo messa. Perché compare quel null? Grazie davvero, ci siamo quasi.. |
![]() |
![]() |
![]() |
#20 |
Senior Member
Iscritto dal: Jan 2004
Città: ROMA
Messaggi: 2055
|
Ho indagato.
In pratica quando lancio il jar, il classPath inizia con "rsrc" invece che con "jar". Non so bene perché, ma la stringa che esce fuori è soltanto "rsrc", che non so da dove esca fuori. Inoltre l'eccezione che esce fuori quando lancio il jar, dopo aver sostituito nel codice la stringa "jar" con "rsrc" (pensando di risolvere) è la seguente: Codice:
Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56) Caused by: java.lang.ExceptionInInitializerError at gui.Login.<clinit>(Login.java:64) at Main.main(Main.java:5) ... 5 more Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -5 at java.lang.String.substring(String.java:1937) at util.PathFinder.parseJarPath(PathFinder.java:67) at util.PathFinder.computeBasePath(PathFinder.java:49) at util.PathFinder.<init>(PathFinder.java:22) at util.PathFinder.<clinit>(PathFinder.java:11) ... 7 more EDIT: Ecco quali sono i valori delle variabili quando lancio il jar: Codice:
String name = thisClass.getSimpleName() + ".class"; String classPath = thisClass.getResource(name).toString(); String fullName = thisClass.getCanonicalName().replace('.', '/'); classPath: rsrcutil/PathFinder.class fullName: util/PathFinder Dopo la seguente istruzione: Codice:
classPath = classPath.substring(0, classPath.indexOf(fullName)); classPath: rsrc fullName: util/PathFinder Ultima modifica di fbcyborg : 17-02-2010 alle 10:22. |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 01:33.