Précédent   Forum des professionnels en informatique > Java > Général Java
Général Java Java SE, Java ME, APIs, Persistance, JDBC, Spring, XML. Avant de poster -> FAQ Java, Sources Java
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 26/01/2012, 11h36   #1
Membre éclairé
 
Homme Michael Tranchant
Chargé de projets JEE - BI
Inscription : septembre 2002
Messages : 37
Détails du profil
Informations personnelles :
Nom : Homme Michael Tranchant
Âge : 30
Localisation : France, Isère (Rhône Alpes)

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

Informations forums :
Inscription : septembre 2002
Messages : 37
Points : 323
Points : 323
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 :
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 :
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 :
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 :
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
mtranchant est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/01/2012, 13h42   #2
Membre Expert
 
Avatar de kolodz
 
Homme Patrick Kolodziejczyk
Étudiant
Inscription : avril 2008
Messages : 441
Détails du profil
Informations personnelles :
Nom : Homme Patrick Kolodziejczyk
Âge : 24
Localisation : France, Val d'Oise (Île de France)

Informations professionnelles :
Activité : Étudiant
Secteur : Enseignement

Informations forums :
Inscription : avril 2008
Messages : 441
Points : 1 559
Points : 1 559
Envoyer un message via MSN à kolodz
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 :
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.
__________________
N'oubliez pas de marquer vos discussions
Si une réponse vous a été utile pensez à voter Pour
Pensez à la javadoc
kolodz est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/01/2012, 14h45   #3
Membre éclairé
 
Homme Michael Tranchant
Chargé de projets JEE - BI
Inscription : septembre 2002
Messages : 37
Détails du profil
Informations personnelles :
Nom : Homme Michael Tranchant
Âge : 30
Localisation : France, Isère (Rhône Alpes)

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

Informations forums :
Inscription : septembre 2002
Messages : 37
Points : 323
Points : 323
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
mtranchant est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 02/02/2012, 11h55   #4
Membre Expert
 
Avatar de kolodz
 
Homme Patrick Kolodziejczyk
Étudiant
Inscription : avril 2008
Messages : 441
Détails du profil
Informations personnelles :
Nom : Homme Patrick Kolodziejczyk
Âge : 24
Localisation : France, Val d'Oise (Île de France)

Informations professionnelles :
Activité : Étudiant
Secteur : Enseignement

Informations forums :
Inscription : avril 2008
Messages : 441
Points : 1 559
Points : 1 559
Envoyer un message via MSN à kolodz
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.
__________________
N'oubliez pas de marquer vos discussions
Si une réponse vous a été utile pensez à voter Pour
Pensez à la javadoc
kolodz est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 00h59.


 
 
 
 
Partenaires

Hébergement Web