Serialization JAVA6 -> JAVA7
Le
news.free.fr
Bonjour,
J'utilise une classe étendant JPanel que je sérialise.
Lors de la dé-sérialisation de JAVA6 -> JAVA7 et inversement, une erreur
InvalidClassException est levée à cause d'une différence de
serialVersionUID dans JComponent.
J'ai éplucher le code, aucune référence à un JComponent n'est faite.
J'utilise uniquement des implémentations de JComponent déclarant
leur propre serialVersionUID.
J'utilise la "Bidouille" suivante pour palier à ce comportement :
public class TGMObjectInputStream
extends ObjectInputStream
{
public TGMObjectInputStream (InputStream is)
throws IOException
{
super (is);
}
/**
* FIXME Grosse magouille.
* {@inheritDoc}
*/
@Override
protected ObjectStreamClass readClassDescriptor ()
throws IOException, ClassNotFoundException
{
ObjectStreamClass descriptor = super.readClassDescriptor ();
Class<?> classLocale = Class.forName (descriptor.getName ());
ObjectStreamClass localDescriptor = ObjectStreamClass.lookup
(classLocale);
if (descriptor.getSerialVersionUID () !=
localDescriptor.getSerialVersionUID ())
descriptor = localDescriptor; // La grosse magouille est là.
return (descriptor);
}
}
=
Avec ce code je fais à nouveau fonctionner les objets java6 sous java7
et inversement.
Cette manœuvre contourne clairement un mécanisme de sécurité de java.
Deux questions :
- Voyez-vous une solution plus élégante ?
- Existe-t-il un outils permettant de trouver à quelle niveau de
l'objet que je sérialise se trouve le JComponent provoquant
l'InvalidClassException.
J'utilise une classe étendant JPanel que je sérialise.
Lors de la dé-sérialisation de JAVA6 -> JAVA7 et inversement, une erreur
InvalidClassException est levée à cause d'une différence de
serialVersionUID dans JComponent.
J'ai éplucher le code, aucune référence à un JComponent n'est faite.
J'utilise uniquement des implémentations de JComponent déclarant
leur propre serialVersionUID.
J'utilise la "Bidouille" suivante pour palier à ce comportement :
public class TGMObjectInputStream
extends ObjectInputStream
{
public TGMObjectInputStream (InputStream is)
throws IOException
{
super (is);
}
/**
* FIXME Grosse magouille.
* {@inheritDoc}
*/
@Override
protected ObjectStreamClass readClassDescriptor ()
throws IOException, ClassNotFoundException
{
ObjectStreamClass descriptor = super.readClassDescriptor ();
Class<?> classLocale = Class.forName (descriptor.getName ());
ObjectStreamClass localDescriptor = ObjectStreamClass.lookup
(classLocale);
if (descriptor.getSerialVersionUID () !=
localDescriptor.getSerialVersionUID ())
descriptor = localDescriptor; // La grosse magouille est là.
return (descriptor);
}
}
=
Avec ce code je fais à nouveau fonctionner les objets java6 sous java7
et inversement.
Cette manœuvre contourne clairement un mécanisme de sécurité de java.
Deux questions :
- Voyez-vous une solution plus élégante ?
- Existe-t-il un outils permettant de trouver à quelle niveau de
l'objet que je sérialise se trouve le JComponent provoquant
l'InvalidClassException.

Poser une question


Il me semble que la réponse est dans l'API :
Class JPanel
java.lang.Object
extended by java.awt.Component
extended by java.awt.Container
extended by javax.swing.JComponent
extended by javax.swing.JPanel
Warning: Serialized objects of this class will not be
compatible with future Swing releases. The current
serialization support is appropriate for short term storage
or RMI between applications running the same version
--
Christian Laborde
La Révolution citoyenne, c'est sur : http://c.lab.over-blog.com/
Les citoyens qui voient Net : http://www.netoyens.info
True E-mail : remove -no-spam-
Sentier des Vinches
CH 1091 Grandvaux
Suisse
Je connaissais cette "faiblesse" de la sérialisation binaire c'est
pourquoi j'utilise exclusivement des implémentations développées en
interne des JComponent.
En d'autres termes, il n'y a aucun raison évidente pour que le
serialVersionUID de JComponent soit utilisé directement lors de la
sérialisation/dé-sérialisation.
De plus, l'astuce que je cite dans le message d'origine fonctionne
correctement. Il ne s'agit donc pas d'un problème d'encodage binaire
mais bien de l'utilisation du serialVersionUID de JComponent alors même
que je ne l'utilise jamais.
Francis JUGE-BOIRARD
Bonjour,
Je suis désolé de déterrer un topic un peu ancien, mais un peu plus de précision dans votre solution me serait d'un grand secours.
Mon problème se situe sur un problème analogue : dans le cadre d'un refactoring par modification de la structure des paquetages, les classes de mes fichiers sérialisés pointent désormais vers une classe qui n'existe plus. Je voulais utiliser le mécanisme pour modifier à la volée le chemin, ce qui devrait être techniquement possible par ce biais :
public class ConvertedObjectInputStream extends ObjectInputStream
{
public ConvertedObjectInputStream(ObjectInputStream in) throws IOException
{
super(in);
}
public ConvertedObjectInputStream(FileInputStream in) throws IOException
{
super(in);
}
@Override
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException
{
ObjectStreamClass descriptor = super.readClassDescriptor();
if (descriptor.getName().equals("oldPkg.myClass"))
{
ObjectStreamClass myOSC = ObjectStreamClass.lookup(newPkg.myClass.class);
return myOSC;
}
return descriptor;
}
}
Cependant, c'est à l'utilisation de cette classe que j'ai un problème. Je construis mon ConvertedObjectInputStream grâce à FileInputStream (si je passe par ObjectInputStream, j'ai un EOFException...) mais lorsque je fais un readObject de ma classe, elle ne passe pas par ma méthode surchargée, comme l'indique ma pile d'erreur :
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 org.steamer.hypercarte.serials.AreaSet.readObject(AreaSet.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)
Une idée sur ce que j'ai pu faire de travers ?
Merci d'avance
Erreur de ma part sur le constructeur, il s'agit de ConvertedObjectInputStream(InputStream in) pas de ConvertedObjectInputStream(ObjectInputStream in)
Le problème reste entier, mais au niveau du readObject de myClass, car il m'est impossible d'instancier ma classe ConvertedObjectInputStream avec un ObjectInputStream ...
mtranchant
C'est sûr ça ? Ça doit dépendre de la liste des endroits où c'est
écrit... Est-ce que le graphe d'objets est complexe ou est-ce qu'il
s'agit d'une simple liste d'objets ?
As-tu regardé si la méthode que tu redéfinis est exécutée ou non à un
moment donné, avec une trace par exemple ?
Il y en a beaucoup des fichiers contenant les données ? Ce sera
peut-être plus facile de les charger avec les anciennes classes, faire
la copie en mémoire dans les nouvelles classes et les resauvegarder
avec ?
Pour les détails de la sérialisation, je ne sais pas trop
malheureusement...
Jeter un oeil sur le code source de la classe ObjectInputStream
fournira peut-être plus de renseignements sur son fonctionnement (la
méthode readObject notamment) ? Peut-être que redéfinir cette méthode
pour y insérer quelques traces pourra fournir des informations plus
précises ?