IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Coder mon ClassLoader


Sujet :

Langage Java

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    315
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 315
    Points : 114
    Points
    114
    Par défaut Coder mon ClassLoader
    Bonjour,

    Ayant parcouru le Net abondamment, je me suis inspiré d'un code pour coder mon ClassLoader afin de charger dynamiquement un .jar.

    Voici le projet : rien de particulier

    clLoader
    src
    pack(package)
    Essai.java
    JarClassLoader.java
    Panneau2.jar
    pourvoir(package)
    Panneau2.class

    Pas d 'erreur à la compilation mais à l'exécution.
    Le .jar que je désire charger est un Panneau simple de type JPanel.

    Je pense qu'il s'agit d'un problème de Thread

    Voici le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package pack;
     
    import javax.swing.JFrame;
     
    public class Essai extends JFrame {
     
        public void name() throws ClassNotFoundException {
            JarClassLoader jcl2 = new JarClassLoader();
            Class cl = jcl2.loadClass("pourvoir.Panneau2");
        }
        public static void main(String[] args) throws ClassNotFoundException {
            try {
                Essai essai = new Essai();
                essai.name();
            } catch (Exception e) {
                System.out.println("TODO: handle exception");
            }
        } 
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    package pack;
     
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.InputStream;
    import java.net.URL;
    import java.util.Enumeration;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
     
    public class JarClassLoader extends ClassLoader {
                private String jarFile = "./Panneau2.jar"; // chemin vers le fichier jar
     
     
                public JarClassLoader() {
                    super(JarClassLoader.class.getClassLoader()); // appel du constructeur parent
                }
     
                public Class loadClass(String className) throws ClassNotFoundException {
                    className = className.replaceFirst("\\.", "/");
                    System.out.println("className dans loadClass : " + className);
                    return findClass(className);
                }
     
                public Class findClass(String className) {
                    byte classBytes[];
                    Class result = null;
     
                    try {
                        JarFile jar = new JarFile(jarFile);
     
                        Enumeration<JarEntry> e = jar.entries();
                        // pour examiner le contenu
                        while (e.hasMoreElements()) {
                            JarEntry jarEntry = (JarEntry) e.nextElement();
                            System.out.println("jarEntry :" + jarEntry);
                        }
                        System.out.println();
     
                        JarEntry entry = jar.getJarEntry(className + ".class");
                        System.out.println("entry ici : " + entry);
     
                        InputStream is = jar.getInputStream(entry);
                        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
                        int nextValue = is.read();
                        while (-1 != nextValue) {
                            byteStream.write(nextValue);
                            nextValue = is.read();
                        }
                        classBytes = byteStream.toByteArray();
                        className = className.replace('/', '.');
                        className = className.replace(".class","");
     
                        System.out.println("className avant define : " + className);
     
                        return defineClass(className, classBytes, 0, classBytes.length);
     
                    } catch (Exception e) {
                        System.out.println("Exception ....");
                        return null;
                    }
                }
    }
    Et la trace avec l'erreur
    className dans loadClass : pourvoir/Panneau2
    Exception in thread "main" jarEntry :META-INF/
    jarEntry :META-INF/MANIFEST.MF
    jarEntry :pourvoir/Panneau2.class

    entry ici : pourvoir/Panneau2.class
    className avant define : pourvoir.Panneau2
    className dans loadClass : javax/swing.JPanel
    jarEntry :META-INF/
    jarEntry :META-INF/MANIFEST.MF
    jarEntry :pourvoir/Panneau2.class

    entry ici : null
    Exception ....
    java.lang.NoClassDefFoundError: javax/swing/JPanel
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:466)
    at pack.JarClassLoader.findClass(JarClassLoader.java:58)
    at pack.JarClassLoader.loadClass(JarClassLoader.java:24)
    at pack.Essai.name(Essai.java:9)
    at pack.Essai.main(Essai.java:14)
    Quelqu'un saurait-il m'indiquer d'où peut venir le problème ?

    Merci d'avance pour votre aide.

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Ton classloader ne fait aucune délégation au classloader du parent.

    De plus, ton code est inutile, le URLClassLoader de java sais déjà lire les jars, il suffit de l'utiliser :/

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    315
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 315
    Points : 114
    Points
    114
    Par défaut ne fait aucune délégation au classloader du parent
    " ton code est inutile, le URLClassLoader de java sais déjà lire les jars,"

    c'est pour le fait d'en construire un moi meme

    "délégation au classloader du parent" , tu peux m'en dire +

  4. #4
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Les classloader doivent toujours déléguer au classloader parent (il sont chainés) afin de pouvoir aussi trouver les classes du parent. Sinon, comme dans ton cas, ce qui n'est pas dans le jar n'est pas accessible (ici JPanel)

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    315
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 315
    Points : 114
    Points
    114
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    jarEntry :META-INF/
    jarEntry :META-INF/MANIFEST.MF
    jarEntry :pourvoir/Panneau2.class
    j'accède aux différentes entrées du .jar

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public JarClassLoader() {
                    super(JarClassLoader.class.getClassLoader()); // appel du constructeur parent
                }
    il me semble que "déléguer au classloader parent " est fait par ce bias

    ?????

  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Je ne parle pas de parent au sens de la heirarchie d'objet, mais de parent au sens du chainage des classloader.


    Tu surcharge loadClass(), mais cette méthode devrait d'abord vérifier si la classe est présente dans le classloader getParent(), ce qu'elle ne fait pas.

    Supprime le code de loadClass (tu n'en a pas besoin) et content toi de surcharger findClass(), ce qui est recommandé dans la javadoc. Là quand on appelle cette méthode, le classloader du parent a déjà été vérifié.

    http://docs.oracle.com/javase/7/docs...a.lang.String)

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    315
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 315
    Points : 114
    Points
    114
    Par défaut
    Ok le loadClass n'est pas nécessaire , compris

    mais il me reste un dernier problème (java.lang.ClassCastException) quand je veux instancier pourvoir.Panneau2

    pourvoir.Panneau2 pan = castedClass.newInstance();

    Merci pour une ultime suggestion

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
     
    package jeter;
     
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.print.Printable;
     
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
    public class Essai extends JFrame {
     
    	JPanel container = new JPanel();
     
    	public Essai() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
    		this.setTitle("Animation");
    	    this.setSize(300, 300);
    	    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	    this.setLocationRelativeTo(null);
     
    	    JarClassLoader jcl2 = new JarClassLoader();
    	    Class<?> cl = jcl2.findClass("pourvoir.Panneau2");
    		//Class cl = jcl2.findClass("pourvoir.Panneau2");
     
    		Class<pourvoir.Panneau2> castedClass = (Class<pourvoir.Panneau2>)cl; 
    		pourvoir.Panneau2 pan = castedClass.newInstance();
     
    	    container.setBackground(Color.white);
    	    container.setLayout(new BorderLayout());
    	    container.add(pan, BorderLayout.CENTER);
    	    this.setContentPane(container);
    	    this.setVisible(true);
    	}
     
     
    	public static void main(String[] args) throws ClassNotFoundException {
     
    		try {
    			Essai essai = new Essai();
    		} catch (Exception e) {
    			System.out.println("TODO: handle exception");
    			e.printStackTrace();
    		}
    	} 
    }

  8. #8
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 552
    Points : 21 608
    Points
    21 608
    Par défaut
    Tu ne peux pas utiliser un ClassLoader customisé pour charger une classe que la classe en cours utilisait déjà -_-°.

    Puisque ta classe Essai utilise la classe pourvoir.Panneau2, la classe pourvoir.Panneau2 a été chargée lors du chargement de la classe Essai, donc bien avant l'appel à jcl2.findClass().

    Vu qu'il sert à ça, findClass() va charger une autre classe pourvoir.Panneau2, qui se trouve ailleurs, et les deux classes pourvoir.Panneau2 chargées indépendamment sont incompatibles et ne peuvent pas être cast l'une en l'autre.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    315
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 315
    Points : 114
    Points
    114
    Par défaut Bonjour thelvin
    Désolé mais je ne comprends plus ....

    Le ClassLoader customisé sert à charger une librairie Panneau2.jar qui se trouve à la racine du projet qui contient un package pourvoir et Panneau2.jar

    Le but étant de pouvoir instancier Panneau2 pan qui me servira ds
    container.add(pan, BorderLayout.CENTER);

    Le but du ClassLoader customisé étant purement à titre d'exercice

    j'ajoute que si je n'utilise pas le classloader customisé, çà marche

    Pourquoi l'un et pas l'autre ?????

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
     URL url = file.toURL();          
    		    URL[] urls = new URL[]{url};
     
     
    		    ClassLoader cl = new URLClassLoader(urls);
     
     
    		    Class<?> cls = Class.forName("pourvoir.Panneau2", true, cl);
     
     
    		    Class<pourvoir.Panneau2> castedClass = (Class<pourvoir.Panneau2>)cls; 
    			pourvoir.Panneau2 pan = castedClass.newInstance();

  10. #10
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 552
    Points : 21 608
    Points
    21 608
    Par défaut
    Charger avec un ClassLoader customisé, une classe qui a déjà été chargée par le ClassLoader par défaut de Java, est un non-sens. Raison pour laquelle ce n'est pas fait pour marcher, et ne marche pas.

    Ton exercice doit se concentrer à charger une classe qui n'existe pas sans ce ClassLoader.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  11. #11
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    pourvoir.Panneau2 doit être supprimé du projet principal, il ne peux pas exister à la fois là et à la fois dans ton classloader personnalisé


    Et ton code devra être

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Class<?> cl = jcl2.findClass("pourvoir.Panneau2");
     
    		JPanel pan = (JPanel)cl.newInstance();
    Le code que tu as mis:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pourvoir.Panneau2 pan = castedClass.newInstance();
    Devrait normalement déclencher une erreur de compilation cat pourvoir.Panneau2 n'existe pas. Or ce n'est pas le cas chez toi (puisque tu sais aller jusqu'au lancement) donc tu as un problème avec tes classloaders.


    Pour te faire un schéma, voilà ce que tu as fait et qui te pose problème


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
                    Bootstrap (java)  ->   base de la jvm (JPanel )
                        |
                        |
                      projet (java)   -> pourvoir.Panneau2, Essai 
                        |                            \
                        |                             |--> Pas les mêmes classes car pas le même classloader, donc pas castables entre elles
                        |                            /
                   JarClassloader (custom) -> pourvoir.Panneau2

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    315
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 315
    Points : 114
    Points
    114
    Par défaut Encore une dernière pour la route ...
    Vos explications me permettent de mieux cerner le classloader mais

    une dernière chose : pourquoi cela fonctionne avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    URL url = file.toURL();          
    		    URL[] urls = new URL[]{url};
     
     
    		    ClassLoader cl = new URLClassLoader(urls);
     
     
    		    Class<?> cls = Class.forName("pourvoir.Panneau2", true, cl);
     
     
    		    Class<pourvoir.Panneau2> castedClass = (Class<pourvoir.Panneau2>)cls; 
    			pourvoir.Panneau2 pan = castedClass.newInstance();

  13. #13
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    la javadoc du constructeur de URLClassLoader que tu utilises (sans argument classloader) l'explique :

    Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.
    Le classloader parent par défaut étant le System class loader, un classloader qui charge justement les classes connues à partir du classpath.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  14. #14
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    315
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 315
    Points : 114
    Points
    114
    Par défaut Merci à tous
    Pour moi une matière difficile à cerner .. mais cette fois c'est OK

    Merci pour vos commentaires éclairés

    A+

  15. #15
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Normalement, si tu n'a redéfini que public Class findClass(String className), ça devrait être le même comportement, Classloader (la class abstraite) délègue par défaut au parent.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 6
    Dernier message: 19/11/2014, 14h06
  2. [XSLT 1.0] Comment coder mon fichier excel en xml
    Par capi81 dans le forum XSL/XSLT/XPATH
    Réponses: 0
    Dernier message: 04/11/2013, 16h26
  3. Java ou Python pour coder mon "Ninjadex"
    Par Lord Mystirio dans le forum Débuter
    Réponses: 2
    Dernier message: 13/03/2011, 18h45
  4. "re"coder mon vieu sony ericsson ?
    Par sinkir dans le forum Java ME
    Réponses: 5
    Dernier message: 12/09/2009, 21h33
  5. Je commence à CODER mon site
    Par °°° Zen-Spirit °°° dans le forum Balisage (X)HTML et validation W3C
    Réponses: 16
    Dernier message: 05/04/2008, 20h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo