11
votes

Readresolve ne fonctionne pas? : Une instance de Serializedform de Guava apparaît

Lors de la désérialisation de l'une de nos données de données (à l'aide du mécanisme par défaut (pas de personnalisé WriteObject / ReadObject)), une instance d'immutablate $ Serializedform (à partir de la bibliothèque Guava de Google) apparaît.

Un tel cas ne doit pas être visible. Des clients de Guava parce que les instances de Serializedform sont remplacées à l'aide de ReadResolve (voir par exemple "Writereplace" dans la classe com.google.common.collect.immutablap).

La désérialisation échoue avec le message suivant: xxx

c'est vrai depuis l'immutablap $ $ Serializedform n'est pas un sous-type de Java.Util.map, mais Cela aurait dû être remplacé. Qu'est-ce qui ne va pas?

Nous n'avons pas de personnalisé WriteObject / ReadObject dans la classe com.bla.C. Nous avons un code de sérialisation personnalisé dans les objets parent (contenant com.bla.c).

Mise à jour, voici le haut de la stacktrace: xxx


1 commentaires

Pouvez-vous montrer la structure complète?


5 Réponses :


3
votes

Veuillez déposer un bogue: http://code.google.com/p/guava-libries/issues/enterry

Si vous pouvez joindre un programme autonome qui déclenche cette erreur pour vous, cela aiderait!


1 commentaires

Je ne pouvais pas reproduire le bogue sur un petit programme autonome.



3
votes

Nous avons trouvé comment éviter le bogue, mais n'a pas trouvé ce qui l'a causé.

Lorsque nous désérialisons une instance de ArrayListMultimap, le chargeur de classe ne trouve pas l'une de nos catégories (COM.BLAH ....), car le chargeur de classe de GUAVA est utilisé (dans le code appelé de ObjectInputStream # Resolveclass) au lieu de la classe par défaut. chargeur. Ensuite, ObjectInputStream propage à la défaillance en remplissant son instance de numéro de téléphone portable avec ClasscastExceptions. De telles exceptions provoquent en fin de compte une réveil à ignorer, ce qui explique pourquoi une formtablap $ Serializedform apparaît.

Ce qui est bizarre, c'est que nous nous sérialisons et désérialisalisons beaucoup d'autres structures de données (nos propres ressources et nos goyaves). Serializing's ArrayListmistMulmulmAmulmAmulmAmulmAp (avec une personnalisée WriteObject) évite le bogue (même si nous sérialisons des instances de collections de GUAVA (non multi-mi-temps)).

Nous ne comprenons pas pourquoi le chargeur de classe devient soudainement faux, mais un bug doit être caché quelque part. Je crois que nous avons reçu CLASSCASTEXCEPPECTION au lieu de la classification de la clastfoundException, car la manipulation des erreurs dans ObjectInputStream est fausse (Readresolve n'aurait pas dû être ignorée même si une classe est manquante).


0 commentaires

7
votes

Cette semaine, nous avons fait face à nouveau ce bug; mais je trouve la raison racine. Le chargeur de classe utilisé par ObjectInputStream dépend fortement du contexte (certains diraient indéterministes em>). Voici la partie pertinente de la documentation de Sun (c'est un extrait de ObjectInputStream # resolveClass (ObjectStreamClass)):

[Le chargeur de classe] est déterminée comme suit: s'il existe une méthode sur la pile de thread courant dont la classe déclarant a été définie par une classe définie par l'utilisateur chargeur (et n'a pas été un produit à mettre en oeuvre des invocations de réflexion), alors il est le chargeur de classe correspondant à la plus proche tel procédé pour la trame en cours d'exécution; autrement, elle est nulle. Si ce résultat d'appel dans un ClassNotFoundException et le nom de l'instance ObjectStreamClass passé est le mot-clé du langage Java pour un type primitif ou vide, l'objet classe représentant ce type primitif ou vide seront retournés (par exemple, un ObjectStreamClass avec le nom « int "sera résolu à Integer.TYPE). Dans le cas contraire, le ClassNotFoundException sera jeté à l'appelant de cette méthode. p> Blockquote>

Dans notre application, nous avons un plug-in Eclipse B qui dépend d'un plug-in utilitaire seulement R. Nous étions objets désérialisation dont les classes sont en B, mais désérialisation a été lancé en A (la création d'un ObjectInputStream là-bas), et qui était le problème. Rarement (à savoir en fonction de la pile d'appel comme le dit doc) désérialisation a choisi le mauvais chargeur de classe (qui ne pouvait pas charger B CLASSIFICATION DES). Pour résoudre ce problème, nous avons passé un chargeur approprié à partir du désérialisation niveau supérieur appelant (en B) à la méthode d'utilité dans A. Cette méthode utilise maintenant un ObjectInputStream personnalisé comme suit (notez la variable libre « loader »): p >

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)) {
                @SuppressWarnings("rawtypes")
                @Override
                protected Class resolveClass(ObjectStreamClass objectStreamClass)
                        throws IOException, ClassNotFoundException {
                    return Class.forName(objectStreamClass.getName(), true, loader);
                }
            };


1 commentaires

J'ai également vu cela dans les environnements OSGI. J'ai eu un paquet responsable de la diffusion [de] sérialisation au niveau de la base de données où je n'avais aucun contrôle sur la manière dont les chargeuses de classe ont été utilisées. En conséquence, j'ai dû définir dynamicimport-package: * Pour que toutes les classes puissent être vues. J'avais malheureusement oublié d'exporter un paquet d'un paquet qui contenait des classes pour désérialiser et plus oublié de mettre en œuvre un proxy de sérialisation dans cette classe. J'ai dirigé un débogueur et moi aussi j'ai vu la classification de la classe de classe survenue. Dommage que cela ne soit pas rapporté!



2
votes

Le problème est que Writereplace () / ReadResolve () ne joue pas bien avec des références circulaires dans votre graphique d'objet. WriterePlace () et Readresolve () sont asymétriques. Pendant la sérialisation, Java remplacera toutes les références, y compris des références circulaires. Mais pendant la désérialisation, Java ne résoudra pas les références circulaires. C'est malheureusement par conception. De La spécification de sérialisation :

Remarque - La méthode ReadResolve n'est pas appelée sur l'objet jusqu'à ce que l'objet est entièrement construit, de sorte de références à cet objet dans sa objet graphique ne sera pas mis à jour sur le nouvel objet nominé par Readresolve. Cependant, lors de la sérialisation d'un objet avec le méthode WriterePlace, toutes les références à l'objet d'origine dans le Le graphique d'objet d'objet de remplacement est remplacé par des références à la objet de remplacement. Donc dans les cas où un objet étant Serialized nomme un objet de remplacement dont le graphique d'objet a un référence à l'objet d'origine, la désérialisation entraînera un graphique incorrect d'objets. En outre, si les types de référence de la objet en cours de lecture (nominée par WriterePlace) et l'objet d'origine ne sont pas compatibles, la construction du graphique d'objet augmentera ClasscastException.

Les développeurs de goyaves pourraient contourner ce problème en faisant de l'immutablaMap $ SerializedForm étendre immutablaMap et déléguez-vous à l'instance d'immutablie appropriée. Lorsqu'une référence circulaire se produit, l'appelant obtiendra la Serializedform au lieu d'une référence directe à l'immutablap, mais c'est mieux qu'une classeCastException.


0 commentaires

1
votes

avait le même problème. Il s'est avéré que la classe des objets membres d'une liste immuable n'était pas sur la classe du côté de la désérialisation. Mais ce fait a été caché derrière la classecastexception.

Maintenant, je fais une meilleure détection d'erreur avec cette construction: xxx


0 commentaires