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

Java Discussion :

Désérialisation et modification de classe à la volée


Sujet :

Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre actif

    Homme Profil pro
    Chargé de projets JEE
    Inscrit en
    Septembre 2002
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Chargé de projets JEE
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2002
    Messages : 41
    Par défaut Désérialisation et modification de classe à la volée
    Bonjour

    Suite à une refactorisation (ré-usinage, pour les puristes ) entrainant une modification de la structure des mes paquetages, une de mes classes sérialisée se retrouve à un autre emplacement.
    Pour illustrer, oldPkg.myClass est ma classe avant le refactoring et newPkg.myClass après le refactoring.

    Le contenu (méthodes, attributs et serialVersionUID) sont strictement identiques.

    Pendant la désérialisation, pas de miracle, je récupère une ClassNotFoundException, ce qui est bien normal.

    Pour éviter çà, je voulais redéfinir à la volée le chemin de mon ancienne classe, par la nouvelle.
    Pour se faire, j'ai créé une classe surchargeant ObjectInputStream, qui entre en jeu pour la désérialisation.
    Cette méthode n'est pas originale, je me suis "inspiré" de ce billet : http://www.norwinter.com/2009/08/20/.../#comment-9635

    Voici donc ma nouvelle classe
    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
     
    public class ConvertedObjectInputStream extends ObjectInputStream
    {
    	public ConvertedObjectInputStream(InputStream in) throws IOException
    	{
    		super(in);
    	}
     
    	@Override
    	protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException
    	{
     
    		ObjectStreamClass descriptor = super.readClassDescriptor();
     
    		// Si oldPkg, alors conversion !
    		if (descriptor.getName().equals("oldPkg.myClass"))
    		{
    			ObjectStreamClass myOSC = ObjectStreamClass.lookup(newPkg.myClass.class);
    			return myOSC;
    		}
     
    		return descriptor;
    	}
    }
    Voici à l'utilisation, dans la classe initiant la désérialisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    FileInputStream fis = new FileInputStream(locator.getFile());
    ConvertedObjectInputStream ois = new ConvertedObjectInputStream(fis);
    try
    {
    	Object myObject = ois.readObject();
    }
    catch (ClassNotFoundException e)
    {
    	e.printStackTrace();
    	/* ... */
    }
    Je construis mon ConvertedObjectInputStream grâce à FileInputStream. Quand je fais un readObject de ma classe, la conversion se passe bien.

    Par contre, myClass possède une surcharge de readObject.
    Elle essaie de recréer les classes, mais sans la conversion, car elle ne connait pas ConvertedObjectInputStream...

    Cette méthode ne reçoit qu'un paramètre : "java.io.ObjectInputStream in"

    myClass
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    	private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
    	{
    		in.defaultReadObject();
    		int nbClasses = in.readInt();
    		clean();
     
    		for (int i = 0; i < nbClasses; i++) {
    			myClass a = (myClass) in.readObject();
    			add(a);
    		}
    		_classRelations = (Hashtable) in.readObject();
    	}
    Si je laisse tel quel, je me retrouve avec le message d'erreur suivant :

    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
    java.lang.ClassNotFoundException: oldPkg.myClass
            at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
            at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
            at java.lang.Class.forName0(Native Method)
            at java.lang.Class.forName(Class.java:247)
            at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:603)
            at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1574)
            at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
            at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
            at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
            at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
            at newPkg.myClass.readObject(myClass.java:210)
            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 java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
            at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
            at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
            at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
            at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
            at newPkg.readingClass.readDataFromSerialFile(readingClass.java:203)
    		...
    Il est donc impossible de réinstancier ma classe ConvertedObjectInputStream car je n'ai qu'un ObjectInputStream, pas un InputStream


    Une idée pour récupérer l'InputStream à partir de ObjectInputStream, afin qu'il soit possible de réinstancier ConvertedObjectInputStream ?
    Un autre biais est-il envisageable ?

    Michael

  2. #2
    Modérateur
    Avatar de kolodz
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    2 209
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 209
    Billets dans le blog
    52
    Par défaut
    J'ai lu en diagonale le problème.

    Il est préférable de ne pas être lié à l'objet en lui-même pour la persistance.
    Surtout lorsque les données doient persisté entre plusieurs versions de l'application.
    Je t'invite à lire :
    http://java.developpez.com/faq/java/..._serialisation
    Mais je pense que le déplacement de la classe est peut-être un peu trop pour la sérialisation.

    Mais le plus simple serai de conserver les deux classes pour faire une conversion assez basique.

    Par exemple :
    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
     
    //Dans un main
    //On lit les objets sérialiser avec l'ancienne classe :
    List<oldPkg.myClass> myListOfOldObjects = this.getListOfOldObjects(myFile);
    List<newPkg.myClass> myListOfNewObjects = this.converteMyOldObjectList(myListOfOldObjects );
    // On sauvegarde la nouvelle liste.
     
    public List<newPkg.myClass> converteMyOldObjectList(List<oldPkg.myClass> list){
     List<newPkg.myClass> toReturn = new  ArrayList<newPkg.myClass>();
    for(oldPkg.myClass old : list){
    //TODO prendre les valeurs de l'ancien objet pour les passer dans le nouveau.
    newPkg.myClass newObject = new newPkg.myClass(old.getValue());
    toReturn .add(old);
    }
    return toReturn ;
    }
    Une fois que tu aura fai la migration tu pourra dire adieu à l'ancienne classe.
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

  3. #3
    Membre actif

    Homme Profil pro
    Chargé de projets JEE
    Inscrit en
    Septembre 2002
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Chargé de projets JEE
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2002
    Messages : 41
    Par défaut
    Bonjour,

    Merci Kolodz pour ta réponse.
    Cependant, reprendre les anciens fichiers pour les "upgrader" n'est pas une option envisageable : je n'ai pas de contrôle sur leur diffusion.

    J'ai abandonné la refactorisation de ce package, pour garder la compatibilité ascendante, en attendant une réflexion plus profonde sur la mise à jour de ce "module" de sérialisation/désérialisation.

    Je laisse le topic ouvert, car il n'est pas résolu, et si quelqu'un a une réponse, çà m'intéresse, ne serait-ce que pour ma connaissance Java-istique.


    Cordialement
    Michael

  4. #4
    Modérateur
    Avatar de kolodz
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    2 209
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 209
    Billets dans le blog
    52
    Par défaut
    Tu peux mettre à disposition de tes utilisateurs un jar de "mise à jour" (convertisseur) de leur sauvegarde.

    Le mieux reste de ne pas faire de sérialisation d'objet pour la persistance.
    Même si cela à un côté pratique dans un premier temps.
    Les applications partagent des données et non pas des objets.

    Cordialement,
    Patrick Kolodziejczyk.
    Si une réponse vous a été utile pensez à
    Si vous avez eu la réponse à votre question, marquez votre discussion
    Pensez aux FAQs et aux tutoriels et cours.

Discussions similaires

  1. Modification de classe à la volée
    Par crazymonkey dans le forum Général Python
    Réponses: 7
    Dernier message: 25/11/2009, 13h58
  2. Réponses: 3
    Dernier message: 04/09/2007, 16h00
  3. modification des pages à la volée
    Par bertil dans le forum Apache
    Réponses: 2
    Dernier message: 28/06/2006, 15h34
  4. [Classloader]mon progr ne marche plus après modif des *.class
    Par Melchisedec dans le forum API standards et tierces
    Réponses: 16
    Dernier message: 30/04/2006, 20h48
  5. Création de classes à la volée...
    Par ouiffi dans le forum Langage
    Réponses: 8
    Dernier message: 16/12/2005, 18h01

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