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 :

ClassLoader & Sérialisation


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre du Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Novembre 2013
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Novembre 2013
    Messages : 7
    Par défaut ClassLoader & Sérialisation
    Bonjour tout le monde,

    Je conçois une application modulaire. J'utilise le ServiceLoader pour charger mes plugins et tout fonctionne correctement.
    Mon problème est le suivant:

    A l'intérieur de l'un de mes plugins, je souhaite charger un fichier de configuration qui est un BEAN serialisé avec XStream ou SnakeYAML.

    J'obtiens un joli "ClassNotFoundException" lorsque mon plugin essaie de déserialisé le fichier (chargé dans l'application principale).
    J'ai validé le fonctionnement par des tests unitaires dans le plugin.

    Je pense que c'est un problème de classLoader même si je ne comprends pas pourquoi..

    Quelqu'un pourrait-il m'éclairer ?

    Merci d'avance.

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Quelle classe? Où est-elle? Comment tu fais tes plugins? Comment tu crée tes Classloader? Comment tu appelle XStream? On peux avoir la trace compète de ton exception?

  3. #3
    Membre du Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Novembre 2013
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Novembre 2013
    Messages : 7
    Par défaut
    Bonjour tchize_,

    merci de jeter un oeil à mon problème

    Voici la classe de l'objet serialsé

    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
     
            public class MongoClusterConfigBean {
     
    	/**
             * Number of maximum connections by entryPoint
             */
    	private int maxPoolSize;
     
    	/**
             * Socket timeout in ms
             */
    	private int socketTimeoutMs;
     
    	/**
             * The database name to connect to
             */
    	private String database;
     
     
     
    	public MongoClusterConfigBean() {
     
    	}
     
    	public int getMaxPoolSize() {
    		return maxPoolSize;
    	}
     
    	public void setMaxPoolSize(int maxPoolSize) {
    		this.maxPoolSize = maxPoolSize;
    	}
     
    	public int getSocketTimeoutMs() {
    		return socketTimeoutMs;
    	}
     
    	public void setSocketTimeoutMs(int socketTimeoutMs) {
    		this.socketTimeoutMs = socketTimeoutMs;
    	}
     
    	public String getDatabase() {
    		return database;
    	}
     
    	public void setDatabase(String database) {
    		this.database = database;
    	}
     
    }
    La serialisation fonctionne comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
              public T loadConfig(File fileToLoad) throws FileNotFoundException {
    		InputStream is = new BufferedInputStream(new FileInputStream(fileToLoad));
    		Object o = this.serializer.unSerialize(is);
    		T object = this.t.cast(o);  // ERREUR de CAST ICI
    		logger.trace("unserialize from yaml file \"" + fileToLoad.getAbsolutePath() + "\" object<" + this.t.getCanonicalName() + ">");
    		return object;
    	}
    C'est ici qu'est générée l'erreur suivante:

    Exception in thread "main" java.lang.ClassCastException: com.tmp.core.engine.mongo.connection.MongoClusterConfigBean cannot be cast to com.tmp.core.engine.mongo.connection.MongoClusterConfigBean
    at com.tmp.core.engine.mongo.facade.MongoDALFactory.init(MongoDALFactory.java:70)
    at com.tmp.core.engine.storage.StorageLoader.load(StorageLoader.java:75)
    at com.tmp.core.engine.bootstrap.AppBootstrap.initiateContainer(AppBootstrap.java:62)
    at com.tmp.core.engine.bootstrap.AppBootstrap.<init>(AppBootstrap.java:24)


    Ce qui est vraiment bizarre c'est que mon objet est chargée, déserialisé correctement, mais ça crash au cast (avec XStream, avec SnakeYAML, j'ai un ClassNotFoundException)

    grace à cet objet :
    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
     
     
    	private XStream stream;
     
            public XMLSerializer() {
    		this.xstream = new XStream();
    	}
     
    	@Override
    	public void serialize(Object o, OutputStream stream) {
    		// TODO Auto-generated method stub
    		this.xstream.toXML(o, stream);
    	}
    	@Override
    	public Object unSerialize(InputStream inputStream) {
    		// TODO Auto-generated method stub
    		return this.xstream.fromXML(inputStream);
    	}
    Mon plugin est chargé de la façon suivante
    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
     
    public IDALFactory load() {
    		if(this.plugin.exists()) {
    			try {
    				URL[] url = new URL[]{this.plugin.toURI().toURL()};
    				URLClassLoader ucl = new URLClassLoader(url);
     
    				ServiceLoader<IDALFactory> serviceLoader = ServiceLoader.load(IDALFactory.class, ucl);
    				Iterator<IDALFactory> iterator = serviceLoader.iterator();
    				if(iterator.hasNext()) {
    					IDALFactory dalfactory = iterator.next();
    					dalfactory.init(this);
    					return dalfactory;
    				}
    				else {
    					logger.error("Impossible to load \"" + this.plugin.getName() + "\"");
    				}
    			} catch (MalformedURLException e) {
    				// TODO Auto-generated catch block
    				logger.error(e.getLocalizedMessage());
    			}
    		}
    		else {
    			logger.error("Storage plugin not found: " + this.plugin.getAbsolutePath());
    		}
    		return null;
    	}
    Désolé pour le bourrage de crane avec tout ce code

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Faudrait voir ce qui se passe dans dans com.tmp.core.engine.mongo.facade.MongoDALFactory.init(MongoDALFactory.java:70)

    Mais pour le problème là (can not be cast), ça veux dire que ta classe MongoClusterConfigBean est présente dans deux classloader, et que tu deserialize en utilisant un classloader, mais que tu essaie de caster en utilisant un autre classloader, donc une autre version de la classe -> l'erreur

    Maintenant, je ne comprend toujours pas

    -> comment tu gère les classloader de tes plugins (tu n'a pas répondu)
    -> Comme tu crée tes deserializer
    -> Où se trouvent, dans ta hierarchie de classloader les classes qui posent problème

  5. #5
    Membre du Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Novembre 2013
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Novembre 2013
    Messages : 7
    Par défaut
    Je vais tenter de répondre à tes questions,

    Pour les classLoaders qui gèrent les plugins, pour le moment, je n'ai qu'un plugin.
    Je crée donc un classLoader pour ce plugin à partir de ma main application.
    Un serviceLoader associé à ce nouveau classLoader s'occupe de m'instancier mon plugin.
    (cf le dernier bloc de code de mon message précédent)
    J'imagine que c'est ici que je fais quelque chose de travers.

    Mon deserializer est une instance de XStream créée depuis mon plugin (avec son classLoader ?)

    Pour la hiérarchie des classLoader, je sais pas comment te répondre..

  6. #6
    Membre du Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Novembre 2013
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Novembre 2013
    Messages : 7
    Par défaut
    J'ai modifié la façon dont je charge mon plugin comme suit:

    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
     
    try {
    				URL[] url = new URL[]{this.plugin.toURI().toURL()};
    				URLClassLoader ucl = new URLClassLoader(url);
     
                                    // Ces trois lignes suivantes ajoutées me permettent d'avoir le meme classLoader dans mon plugin et dans la main App, mais est ce une bonne chose ?
    				ClassLoader originalClassLoader = ClassLoader.getSystemClassLoader();
    				ClassLoader ncl = new JarSeekingURLClassLoader(this.plugin, originalClassLoader);
    				Thread.currentThread().setContextClassLoader(ncl);
     
    				ServiceLoader<IDALFactory> serviceLoader = ServiceLoader.load(IDALFactory.class, ucl);
    				Iterator<IDALFactory> iterator = serviceLoader.iterator();
     
    				logger.warn(Thread.currentThread().getContextClassLoader());
     
    				if(iterator.hasNext()) {
    					IDALFactory dalfactory = iterator.next();
    					dalfactory.init(this);
    					return dalfactory;
    				}
    				else {
    					logger.error("Impossible to load \"" + this.plugin.getName() + "\"");
    				}
    			} catch (MalformedURLException e) {
    				// TODO Auto-generated catch block
    				logger.error(e.getLocalizedMessage());
    			}
    Et je log l'id de l'object classLoader dans mon plugin (à l'instanciation avant chargement de sa config -> qui plante) et l'id du classLoader de l'application main

    ils sont identiques:

    15:03:55.976 [main] WARN com.tmp.core.engine.storage.StorageLoader - com.tmp.core.engine.storage.JarSeekingURLClassLoader@1f1740f
    15:03:55.981 [main] WARN com.tmp.core.engine.mongo.facade.MongoDALFactory - com.tmp.core.engine.storage.JarSeekingURLClassLoader@1f1740f


    je comprends plus trop du coup..

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par dterranova Voir le message
    Je crée donc un classLoader pour ce plugin à partir de ma main application.
    Un serviceLoader associé à ce nouveau classLoader s'occupe de m'instancier mon plugin.
    (cf le dernier bloc de code de mon message précédent)
    Désolé, je n'avais pas vu ce bout de code. Visiblement, tu ne charge qu'un jar
    J'imagine que c'est ici que je fais quelque chose de travers.
    Citation Envoyé par dterranova Voir le message
    Mon deserializer est une instance de XStream créée depuis mon plugin (avec son classLoader ?)
    Ben c'est à toi de nous le dire, on a pas ton code pour le moment qui fait ça Il y a plein de manière de créer un deserializer. Par défaut, il utilisera le ContextClassLoader je pense qui, a moins que tu aie fait quelque chose de particulier, n'est pas le classloader de ton plugin.


    Donc pour refenir à ton message (Class cast exception), tu as un classe Machin chargée depuis le plugin, probablement présente dans le jar du plugin, mais aussi une classe Machin présente dans ton application principale. XStream utilise la définition présente dans ton application principale, alors que quand tu fait le cast juste après, tu utilise la définition du plugin => clash.

    Vérifie que
    1) ta classe ne serait pas présente dans 2 jar
    2) les classes du plugin n'ont pas été chargée aussi par l'application principale. C'est une erreur commune d'avoir un dossier contenant tous les jar de l'application, puis d'essayer de recharger un de ces jar une deuxième fois avec un URLClassLoader comme plugin => ça deviens le bordel.
    Citation Envoyé par dterranova Voir le message
    Pour la hiérarchie des classLoader, je sais pas comment te répondre..
    C'est bon, j'avais pas vu le bout de code. Mais ce serait bien de te faire une liste de quel jar se trouve à quel niveau. Si tu n'arrive pas a avoir une idée claire là dessus, tu va devant de grosses emmerdes

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

Discussions similaires

  1. [Serializable][image] Sérialiser une image
    Par Galima dans le forum Graphisme
    Réponses: 7
    Dernier message: 01/07/2004, 18h12
  2. Sérialisation avec sockets
    Par sebi77 dans le forum Entrée/Sortie
    Réponses: 4
    Dernier message: 03/05/2004, 20h24
  3. SGBD ou sérialisation
    Par tiboleo dans le forum Décisions SGBD
    Réponses: 3
    Dernier message: 07/10/2003, 16h18

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