Différence de chargement d'une API entre exécution class et Jar

Différence de chargement d'une API entre exécution class et Jar - Java - Programmation

Marsh Posté le 05-03-2012 à 14:58:47    

Bonjour à tous [:dawa]
 
Je cherche à savoir pourquoi l'exécution d'un programme est différente selon le mode d'exécution (class ou Jar).
 
Le code en question ajoute un répertoire au path courant en vue du chargement d'une librairie (DLL Win 32) par une API tierce (Sun Comm). On trouve l'arborescence suivante :
 

test-app
---bin/win32com.dll
---lib/comm.jar
---classes/
---target/test-app.jar


 
et le code
 

Code :
  1. package com.foo;
  2.  
  3. public class ComTester {
  4.  
  5.    static {
  6.        System.out.println("Modifying PATH..." );
  7.        try {
  8.            addDir("\\absolute\\path\\to\\test-app\\bin" );
  9.        } catch (IOException e) {
  10.            System.err.println(e.getMessage());
  11.        }
  12.        System.out.println("Setting properties..." );
  13.        System.setProperty("Driver", "com.sun.comm.Win32Driver" );
  14.    }
  15.  
  16.    public static void main(String[] args) {
  17.        final Enumeration<CommPortIdentifier> comPorts = CommPortIdentifier.getPortIdentifiers();
  18.        while (comPorts.hasMoreElements()) {
  19.            System.out.println("Port: " + comPorts.nextElement().getName());
  20.        }
  21.    }
  22.  
  23.    private static void addDir(String s) throws IOException {
  24.        // java.library.path ne peut être settée durant l'exécution, cette méthode permet de passer outre la limitation
  25.    }
  26.  
  27. }


 
Lorsque j'exécute ce code via  
 

Code :
  1. SET CLASSPATH=.
  2. SET CLASSPATH=%CLASSPATH%;.\lib\comm.jar
  3. SET CLASSPATH=%CLASSPATH%;.\classes
  4.  
  5. java -classpath %CLASSPATH% com.foo.ComTester


 
youpi c'est la fête, les ports série et parallèle dispos sur la machine sont affichés.
 
Par contre, via :
 

Code :
  1. java -jar .\target\test-app.jar


 
avec le class-path correctement setté dans le Manifest, ça ne fonctionne plus, sans message d'erreur (les autres printf sont affichés normalement, tout se passe comme si il n'y avait pas de ports dispos sur la machine, signe pour cette API qu'il y a un souci au chargement).
 
Pour les deux types d'exécution :
- le class-path est correct, puisque quand il ne l'est pas, le code lance une ClassNotFoundException.
- l'API tierce (comm.jar) se charge bien du System.loadLibrary() sur la DLL, puisque quand cette dernière n'est pas ajoutée au PATH, le premier appel à l'API lance une UnsatisfiedLinkError.
 
Piste sans doute intéressante : lorsque le Jar de l'API tierce est renommé (comm.jar vers comm-foo.jar par exemple), aussi bien l'exécution class que Jar ne renvoient pas de résultat (les printf, pas de message d'erreur mais pas de port affiché). C'est le comportement du Jar lorsque le class-path est correctement renseigné.
 
Le comportement du classloader est pourtant identique du point de vue de l'API tierce que l'on exécute par class ou par Jar (vérifié par en diff entre les exécutions en -verbose).
 
Des idées  [:agkklr]  
 

Reply

Marsh Posté le 05-03-2012 à 14:58:47   

Reply

Marsh Posté le 08-03-2012 à 13:40:43    

J'aurais aimé upper avec des infos plus constructives, mais vraiment je sèche.
 
Je rappelle que le problème peut se résumer à "pourquoi deux moyens d'exécution différents (class et jar) d'une même application renvoient deux résultats différents ?"
 
 [:fegafobobos:2]

Reply

Marsh Posté le 14-03-2012 à 15:07:05    

Je vais tenter une explication différente.
 
Soit le bout de code précédent qui fait un appel tout bête à une API (comm.jar).
 
Pour que cela fonctionne (selon la doc), il faut que le fichier javax.com.properties soit situé dans JAVA_HOME/lib. On peut donc penser que l'API comm fait quelque part un properties.load sur le path absolu en se servant du JAVA_HOME.
 
Sauf que ce n'est pas le cas, puisqu'en exécutant mon programme avec  
 

# java -classpath ".\classes;.\comm.jar" com.MaClasse


 
Ça fonctionne si le .properties est dans le répertoire courant (on va appeler ça le Cas 1, qui fonctionne). Pourtant, nulle part je ne set le classpath avec le répertoire courant. Ce que prouve à mon sens le Cas 1, c'est que le .properties n'est aps chargé avec un chemin absolu, preuve qu'il est potentiellement possible de le charger de n'importe quelle manière.
 
Encore plus fort, si je met le .properties dans un autre répertoire et que je l'inclus au classpath de la commande précédente, ça ne fonctionne pas (Cas n° 2). Ça ça m'intrigue, je n'ai pas d'explication.
 
Si je reprend le cas n°1 (celui qui fonctionne) en modifiant le nom du Jar de l'API, ça ne fonctionne plus :
 

# java -classpath ".\classes;.\commX.jar" com.MaClasse


 
pas d'explication non plus là dessus, appelons le cas n°3.
 
Enfin, si je reprend le cas n°1 (qui fonctionne, .properties à la racine) avec un Jar exécutable (et le répertoire courant ajouté au classpath dans le manifest de celui-ci), ça ne fonctionne pas non plus. C'est le cas n°4, et potentiellement mon objectif à terme.
 
 [:rarnomix:4]

Reply

Marsh Posté le 15-03-2012 à 10:41:18    

par curiosité, j'ai googlé ce matin et j'avais pas mal de références pointant des limitations de classloading et apparemment, il y a qques custom class loader utils qui ont été créés pour être plus puissants que le classloader par défaut.
 
tu es p-ê à la limite de leur modèle générique ?


---------------
A straight line is a special case of a curve. It's a curve which is uncurved. -- Susskind.
Reply

Marsh Posté le 15-03-2012 à 10:44:32    

Si je te suis, la (une) limitation du classloader amènerais un chargement différent des dépendances en fonction du mode d'exécution (Jar ou class) ?
 
Ça me marrait plus un bug qu'une limitation sans plus de détail sur le fonctionnement.
 
De plus, j'ai vraiment pas l'impression de monter un cas complexe, c'est aussi pour ça que je suis si étonné de ne pas trouver d'explications sur mon problème.

Reply

Marsh Posté le 15-03-2012 à 11:46:29    

c'est l'impression que j'avais en lisant 2~3 posts trouvés ce matin (java limitation classloader native dll)


Message édité par TBone le 15-03-2012 à 11:46:36

---------------
A straight line is a special case of a curve. It's a curve which is uncurved. -- Susskind.
Reply

Marsh Posté le 15-03-2012 à 12:02:04    

Autant les liens sont intéressants, autant j'ai l'impression que mon problème se situe en amont (ce que je tente d'expliquer dans mon 3ème post).
 
Avant même le chargement de la lib (qui lui ne me pose pas de problème), c'est le chargement des properties en fonction du contexte d'exécution qui me laisse perplexe. Ça et le cas n°3 (dépendance renommée) qui me laisse pantois.

Reply

Marsh Posté le 15-03-2012 à 14:16:53    

Non mais euh au lieu de faire dans l'empirique, t'as maté les sources de comm.jar pour voir comment le bordel est initialisé?


---------------
Hey toi, tu veux acheter des minifigurines Lego, non ?
Reply

Marsh Posté le 15-03-2012 à 14:18:35    

je pense à un cas où bosser à partir du jar dézippé ou du jar zippé peut poser problème
c'est si le jar est signé
le manifest contient alors la signature de chaque classe contenue dans le jar
 
je connais pas le mécanisme, ni la manifestation des conséquences mais c'est le seul cas qui me vient à l'esprit :o

Reply

Marsh Posté le 15-03-2012 à 14:29:06    

je viens de downloader je jar, c'est bien ça, il est signé
 
donc dans le cul lulu [:joce]
 

Citation :


Manifest-Version: 1.0
 
Name: javax/comm/CommApiVersion.class
Digest-Algorithms: SHA MD5  
SHA-Digest: CSOMI+C9ISEONZBjmFTZIlf8WtU=
MD5-Digest: blPX0ZKb/B5m4c1Mhdm9zg==
 
Name: javax/comm/CommPort.class
Digest-Algorithms: SHA MD5  
SHA-Digest: CZ174fF9GP8jYA5Ky/IVJpOQJoA=
MD5-Digest: qbiZCI9rBFvZBKyVPvG3+A==
 
Name: javax/comm/UnsupportedCommOperationException.class
Digest-Algorithms: SHA MD5  
SHA-Digest: gSjNRfsrAWg8yGAUk826tBWoVUo=
MD5-Digest: q/MR4GH3yZE/dBGAFe8yiA==
 
Name: javax/comm/CommPortIdentifier.class
Digest-Algorithms: SHA MD5  
SHA-Digest: l6w355OtTFoT07OxOdZB4iQI5S8=
MD5-Digest: Tz7Z3AmL4lCSUbve85hSCA==
 
Name: javax/comm/CommPortEnumerator.class
Digest-Algorithms: SHA MD5  
SHA-Digest: Q9eNg+V5PFJ0JHLr36rc3dBDqIc=
MD5-Digest: 03zK8dD9TMsD5EVykc//yA==
 
Name: javax/comm/OwnershipEventThread.class
Digest-Algorithms: SHA MD5  
SHA-Digest: oXXE5xNTNao3xK8EiM6MIEgshAg=
MD5-Digest: afdaGmh3wgVsB90Nk7cH+w==
 
Name: javax/comm/CpoListEntry.class
Digest-Algorithms: SHA MD5  
SHA-Digest: v7V6QI3mhGptIZLW9/yQl/6Kbko=
MD5-Digest: iLHSfyYhSZf2EqicdNjZVA==
 
Name: javax/comm/CpoList.class
Digest-Algorithms: SHA MD5  
SHA-Digest: W8+5dB64uDptF0BkHeW0l+eVkDg=
MD5-Digest: EoFwQK1MLCeKeXASh17qeQ==
 
Name: javax/comm/NoSuchPortException.class
Digest-Algorithms: SHA MD5  
SHA-Digest: Gg6ceMWxZyK2FpCQtU42b3Sj2B4=
MD5-Digest: Dugx+nCP07hhdLlgab3cGg==
 
Name: javax/comm/PortInUseException.class
Digest-Algorithms: SHA MD5  
SHA-Digest: 3fzYbJoluTRq0BaFMZG8iz0OhUY=
MD5-Digest: +WVbywrCiMbvkNNUEPWeUA==
 
Name: javax/comm/CommDriver.class
Digest-Algorithms: SHA MD5  
SHA-Digest: hIw+XhvpqZgqQ9qDPhyQCQ0HvKU=
MD5-Digest: 2WJtEmYYOdeyKN2h/sT8wg==
 
Name: javax/comm/CommPortOwnershipListener.class
Digest-Algorithms: SHA MD5  
SHA-Digest: X7HUXg0sasFO583TuTwaEMUfkpc=
MD5-Digest: yni36Ax+THZBwMCDOuKrbQ==
 
Name: javax/comm/ParallelPort.class
Digest-Algorithms: SHA MD5  
SHA-Digest: 6E0PX0T8u2CTs/ys8EvN913ducA=
MD5-Digest: o1HbGwzDeo3xlN3dxAlyXA==
 
Name: javax/comm/ParallelPortEventListener.class
Digest-Algorithms: SHA MD5  
SHA-Digest: pJQrKJJ9P9Edyd16tRw08BDBpY0=
MD5-Digest: rp61COGXhkeykV3qqXr3XQ==
 
Name: javax/comm/ParallelPortEvent.class
Digest-Algorithms: SHA MD5  
SHA-Digest: hTzxyl0Lna6+cq7nGOpzlLg89to=
MD5-Digest: vBOhMtnFKF/PbwQGXEcpMA==
 
Name: javax/comm/SerialPort.class
Digest-Algorithms: SHA MD5  
SHA-Digest: 82Z6dQjCbJCah4XjGKMDu0US154=
MD5-Digest: EkvH7uzyDZKzWVZDDZNoiA==
 
Name: javax/comm/SerialPortEventListener.class
Digest-Algorithms: SHA MD5  
SHA-Digest: XX7cVoEfi3sKI6Q2sF96vp6oiO0=
MD5-Digest: Fr5RvX1m+vjyw4Yu/7rL/Q==
 
Name: javax/comm/SerialPortEvent.class
Digest-Algorithms: SHA MD5  
SHA-Digest: gc29eSz7kebzyWyLCoVRa3JS23s=
MD5-Digest: cBAu9/WhWqne7o9TD2aTUQ==
 
Name: com/sun/comm/Win32ParallelPort.class
Digest-Algorithms: SHA MD5  
SHA-Digest: NC9kBLn1uBRB1KbRoohldYDEo5Q=
MD5-Digest: 1ifoDVImvM+ahLure6rOEA==
 
Name: com/sun/comm/Win32SerialPort.class
Digest-Algorithms: SHA MD5  
SHA-Digest: zBKgH10BlWeX7I1lHrzBYjNSB4o=
MD5-Digest: 933f/iK3Le5kgHcbmI4Vuw==
 
Name: com/sun/comm/NotificationThread.class
Digest-Algorithms: SHA MD5  
SHA-Digest: vrtVqPfwxc4E6jYm/cu1fqZ2Rec=
MD5-Digest: oHcuoSje6czs7DfYsF9BNQ==
 
Name: com/sun/comm/Win32SerialOutputStream.class
Digest-Algorithms: SHA MD5  
SHA-Digest: vxPZidVSHauSm7IvKCsr+Mow0qE=
MD5-Digest: JMGWTm9Wpy/Wgv9YXwsVwA==
 
Name: com/sun/comm/Win32SerialInputStream.class
Digest-Algorithms: SHA MD5  
SHA-Digest: E5JZzbbXFQpyHU8VOLQbe4m3FQ0=
MD5-Digest: kNOFMsUR2tWTEJ7F/Jx2gw==
 
Name: com/sun/comm/Win32Driver.class
Digest-Algorithms: SHA MD5  
SHA-Digest: psB0SnlOV9BGbI20kan/89hPBzM=
MD5-Digest: EpFegd7B6ZzjmLSe+Fzo9A==
 


 
sinon je vois pas pourquoi tu veux absolument dézipper le comm.jar, ça n'a pas d'intérêt, et ça t'a même fait perdre ton temps :o

Message cité 1 fois
Message édité par basketor63 le 15-03-2012 à 14:31:39
Reply

Marsh Posté le 15-03-2012 à 14:29:06   

Reply

Marsh Posté le 15-03-2012 à 14:34:05    

the real moins moins a écrit :

Non mais euh au lieu de faire dans l'empirique, t'as maté les sources de comm.jar pour voir comment le bordel est initialisé?


 
Si elles étaient dispos, j'aurais effectivement commencé par là [:petrus75]
 
J'ajoute :
 

Citation :

Q: I would like to port the Java communications API to my favorite platform. Can you give me the source code to the reference implementations to help me out?
 
A: Sun will not be releasing the source code to reference implementations. Successful porting efforts are underway without the reference implementation source code (see previous answer).


 
( http://java.sun.com/products/javac [...] index.html )
 

basketor63 a écrit :

je pense à un cas où bosser à partir du jar dézippé ou du jar zippé peut poser problème
c'est si le jar est signé
le manifest contient alors la signature de chaque classe contenue dans le jar
 
je connais pas le mécanisme, ni la manifestation des conséquences mais c'est le seul cas qui me vient à l'esprit :o


 
Le code que dont je fais varier l'exécution (soit en Jar soit en classe) est de mon côté, et pas signé quand il est sous forme de Jar.
comm.jar est effectivement un Jar signé, mais est lui toujours appelé sous cette forme (i.e. quand mon code est sous forme de classes/*.class, comm.jar est intégré au classpath => ça fonctionne).

Reply

Marsh Posté le 15-03-2012 à 14:36:54    

basketor63 a écrit :

sinon je vois pas pourquoi tu veux absolument dézipper le comm.jar, ça n'a pas d'intérêt, et ça t'a même fait perdre ton temps :o


 
Bin je veux pas  [:leg10]  
 
Tu peux restreindre ce qui marche pas / ce que je veux faire à mon 3ème post, à aucun moment il est question d'exploder les dépendances.

Reply

Marsh Posté le 15-03-2012 à 14:42:37    

mais ça répond à ton cas 3 je pense
 

LeRiton a écrit :


# java -classpath ".\classes;.\comm.jar" com.MaClasse


 

# java -classpath ".\classes;.\commX.jar" com.MaClasse


 
pas d'explication non plus là dessus, appelons le cas n°3.


Reply

Marsh Posté le 15-03-2012 à 14:46:35    

LeRiton a écrit :

Lorsque j'exécute ce code via  
 

Code :
  1. SET CLASSPATH=.
  2. SET CLASSPATH=%CLASSPATH%;.\lib\comm.jar
  3. SET CLASSPATH=%CLASSPATH%;.\classes
  4.  
  5. java -classpath %CLASSPATH% com.foo.ComTester


 
youpi c'est la fête, les ports série et parallèle dispos sur la machine sont affichés.
 
Par contre, via :
 

Code :
  1. java -jar .\target\test-app.jar




 
ça je crois que c'est "normal" que ça marche pas
 

Code :
  1. java -jar .\test-app.jar


 
ça ça doit marcher [:joce]
 

Code :
  1. <plugin>
  2.    <groupId>org.apache.maven.plugins</groupId>
  3.    <artifactId>maven-jar-plugin</artifactId>
  4.    <version>2.3.2</version>
  5.    <configuration>
  6.     <packagingIncludes>*.jar</packagingIncludes>
  7.     <archive>
  8.      <manifest>
  9.       <mainClass>com.nique.ta.race.Main</mainClass>
  10.       <packageName>com.nique.ta</packageName>
  11.       <addClasspath>true</addClasspath>
  12.       <classpathPrefix>dependencies/</classpathPrefix>
  13.      </manifest>
  14.     </archive>
  15.     <excludes>
  16.      <exclude>*.properties</exclude>
  17.     </excludes>
  18.    </configuration>
  19.   </plugin>


 
 
je me suis arraché les cheveux sur ce genre de trucs déjà, et il y a des comportements bizarre au niveau des chemins relatifs de class path
 
j'ai de plus jamais vraiment réussit à utiliser -classpath %CLASSPATH_LIB% -jar %JAR% conjointement
nottament à cause du fait qu'il faut spécifier le nom de chaque jar dans %CLASSPATH_LIB% ce qui est bien lourd
du coup à ce moment là autant faire en sorte que maven génère automatiquement le classpath dans le manifest
 
mais du coup on ne peut plus gérer dynamiquement l'emplacement des librairies dépendentes
bref, c'est la merde pour avoir un truc clef en main et facile à maintenir
 
 
je pense que tu donnes pas assez de détails
il faudrait ton pom, le contenu de ton manifest généré etcetera

Message cité 1 fois
Message édité par basketor63 le 15-03-2012 à 14:55:13
Reply

Marsh Posté le 15-03-2012 à 15:09:33    

basketor63 a écrit :

ça je crois que c'est "normal" que ça marche pas


 
C'est pas un problème de classpath (au sens entrée dans le manifest), celui-ci est bon et a été vérifié (et re vérifié) dans tous les tests que je mentionne.
Si il y a une coquille, considère que c'est dans la rédaction de mes posts (ou plus exactement que les tests du troisième post sont indépendants de ceux du premier).
 

basketor63 a écrit :

j'ai de plus jamais vraiment réussit à utiliser -classpath %CLASSPATH_LIB% -jar %JAR% conjointement
nottament à cause du fait qu'il faut spécifier le nom de chaque jar dans %CLASSPATH_LIB% ce qui est bien lourd


 
C'est surtout dû au fait qu'en cas d'exécution par le commutateur -jar, la seule entrée de classpath prise en compte est celle du manifest, donc ce que tu poses après ton -cp est ignoré.
 
Je répète, je vous aurais certainement pas tanné avec un bête soucis de classpath mal setté, les chemins sont corrects lors de mes tests et devraient en toute logique (la mienne en tout cas) fonctionner en l'état.
 
Par contre, ton hypothèse sur le Jar signé qui ne "fonctionne" plus une fois renommé, ça se tient :jap:
 
 
 

Reply

Marsh Posté le 15-03-2012 à 15:21:04    

mais c'est un peu bancal, car je vois pas mention du nom du jar dans le manifest

Reply

Marsh Posté le 15-03-2012 à 15:31:04    

basketor63 a écrit :

mais c'est un peu bancal, car je vois pas mention du nom du jar dans le manifest


 
Mais qu'est-ce qui est bancal  [:fegafobobos:2]  
 
Je t'ai dis d'oublier mon premier post et de ne raisonner que sur le 3ème. Dans celui-ci, je ne donne pas de manifest, donc c'est vite réglé.
 

Citation :

Enfin, si je reprend le cas n°1 (qui fonctionne, .properties à la racine) avec un Jar exécutable (et le répertoire courant ajouté au classpath dans le manifest de celui-ci), ça ne fonctionne pas non plus. C'est le cas n°4, et potentiellement mon objectif à terme.


 
Dans cette phrase, je sous-entend évidemment que le minimum syndical (les dépendances dans le classpath, ou bundlées dans le Jar) est fait.
 
 

Reply

Marsh Posté le 15-03-2012 à 17:03:16    

si jamais je l'ai pas encore dit, le truc que j'avais utilisé a l'epoque pour remplacer comm.jar c'était RXTX. Possiblement pas beaucoup mieux, mais open source.


---------------
Hey toi, tu veux acheter des minifigurines Lego, non ?
Reply

Marsh Posté le 15-03-2012 à 17:57:44    

LeRiton a écrit :

Je suis pas au courant, mais si ça existe je suis plus que preneur !
 
Edit : RXTX demande une copie dans les mêmes répertoires que javax.comm, serialio est payant, jSSC demande que le projet final embarque l'une des DLL.


 
:D

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed