web-dev-qa-db-fra.com

La dernière bibliothèque Open JDK 8 JAXB ne parvient pas à disséminer les objets avec des propriétés contenant de nouveaux caractères de ligne

J'utilise Java sur Ubuntu 16.04. J'ai récemment effectué une mise à niveau vers Open JDK Java version "1.8.0_161" installée à l'aide du package du programme d'installation Oracle-Java8 (version du package 8u161-1 ~ webupd8 ~ 0). Depuis cette mise à niveau, je reçois de nouvelles exceptions lors du marshalling JAXB d'objets Java.

Plus précisément, lors de la tentative d'utilisation de JAXB pour marshaler un objet Java en XML, j'obtiens l'exception suivante si l'objet Java a une propriété String contenant des caractères de nouvelle ligne ("\ n") et que cette propriété String est sérialisée en tant que contenu d'élément dans le fichier. XML. (Par ailleurs, si la propriété String est sérialisée en tant que contenu d'attribut, tout caractère de nouvelle ligne dans la valeur de String est converti en caractère d'espacement et l'exception n'est pas déclenchée.)

Ce qui semble se passer est que

com.Sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput $ NewLineEscapeHandler.escape

convertit le caractère de nouvelle ligne de la propriété String de l'objet Java en référence à l'entité 
. Cette référence d'entité est ensuite écrite dans le flux de sortie XML, mais lors de la vérification du nom de la référence de l'entité, l'exception est levée car #xa n'est pas reconnue en tant que nom de référence d'entité valide.

Est-ce le comportement attendu? Si oui, que dois-je faire pour conserver les caractères de nouvelle ligne dans la sérialisation de l'objet Java? Si non, que dois-je faire pour contourner ce problème? 

La partie pertinente de la trace de pile est:

... Caused by: javax.xml.stream.XMLStreamException: Invalid name start character '#' (code 35) (name "#xa")
at com.fasterxml.aalto.out.XmlWriter.throwOutputError(XmlWriter.Java:472)
at com.fasterxml.aalto.out.XmlWriter.reportNwfName(XmlWriter.Java:383)
at com.fasterxml.aalto.out.ByteXmlWriter.verifyNameComponent(ByteXmlWriter.Java:235)
at com.fasterxml.aalto.out.ByteXmlWriter.constructName(ByteXmlWriter.Java:181)
at com.fasterxml.aalto.out.WNameTable.findSymbol(WNameTable.Java:324)
at com.fasterxml.aalto.out.StreamWriterBase.writeEntityRef(StreamWriterBase.Java:615)
at net.galexy.fieldguide.jaxb.CustomXMLStreamWriter.writeEntityRef(CustomXMLStreamWriter.Java:198)
at com.Sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput$XmlStreamOutWriterAdapter.writeEntityRef(XMLStreamWriterOutput.Java:277)
at com.Sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput$NewLineEscapeHandler.escape(XMLStreamWriterOutput.Java:242)
... 60 more

Par exemple, si je m’identifie le code XML suivant:

<?xml version='1.0' encoding='UTF-8'?>
<description>
   <note>The text of the note</note>
</description>

et essayez ensuite de le redéfinir en XML, aucune exception n’est levée.

Si, toutefois, il y a une nouvelle ligne au milieu du contenu de la note:

<?xml version='1.0' encoding='UTF-8'?>
<description>
   <note>The text of
         the note</note>
</description>

Ensuite, l'exception est levée.

Le contexte JAXB utilisé est com.Sun.xml.internal.bind.v2.runtime.JAXBContextImpl.

Le marshaller JAXB utilisé est com.Sun.xml.internal.bind.v2.runtime.MarshallerImpl

En cherchant plus d'informations sur les modifications, je suis tombé sur le rapport de bogue suivant suggérant que d'autres utilisateurs ont rencontré le même changement avec cette version de JAXB:

JDK-8196491 Les nouvelles lignes dans les chaînes JAXB des demandes SOAP sont échappées jusqu'à "& # xa;"

La réponse à cette question de débordement de pile suggère que je puisse reprendre le contrôle de l'évasion de personnage en obligeant mon agent marshaller à utiliser une implémentation personnalisée de com.Sun.xml.bind.marshaller.CharacterEscapeHandler.

Cela me laisse perplexe car javax.xml.bind.Marshaller ne semble pas déclarer un nom de propriété statique com.Sun.xml.bind.marshaller.CharacterEscapeHandler alors qu'il déclare d'autres noms de propriété tels que Marshaller.JAXB_FORMATTED_OUTPUT, ce qui équivaut à "jaxb.formatted.output.

Même si je pouvais demander au marshaller d’utiliser mon gestionnaire d’échappement de personnage personnalisé, je ne suis pas tout à fait sûr de ce que je devrais faire dans ce gestionnaire d’échappement. Existe-t-il un gestionnaire d’échappement de base approprié que je puisse remplacer pour hériter de la gestion des échappements standard, en veillant à ce que j’intervienne pour empêcher l’échappement des caractères de nouvelle ligne?

J'ai également essayé Oracle Java 9 (package version 9.0.4-1 ~ webupd8 ~ 0) et cette version de Java présente les mêmes problèmes.

J'ai également essayé la prochaine version d'Oracle Java 8 (1.8.0_162) et cette version présente les mêmes problèmes.

Le téléchargement d'une version antérieure de Java à partir du site Web d'Oracle (1.8.0_152) résout le problème, mais ne constitue pas un moyen satisfaisant de le résoudre.

6
Geoff S

Dans mon cas, j'utilise JAXB pour convertir quelques objets en XML et les sérialiser dans un fichier, via StAX/WoodStox. J'ai réussi à résoudre le problème en filtrant le XML en cours de sérialisation. En détail, l'approche est comme:

1) Définissez un StreamWriter2Delegate personnalisé, remplacez writeEntityRef() , de sorte que, lorsque cette méthode reçoit le code d'entité incorrect (#xd ou #xa), elle appelle son délégué pour réécrire le caractère d'origine (c'est-à-dire, \n ou \r), qui n'a pas réellement besoin d'être échappé:

@Override
public void writeEntityRef ( String eref ) throws XMLStreamException
{
    if ( eref == null || !eref.startsWith ( "#x" ) ) {
        super.writeEntityRef ( eref );
        return;
    }
    String hex = eref.substring ( 2 );
    for ( char c: new char[] { '\r', '\n' } )
        if ( Integer.toHexString ( c ).equals ( hex ) ) {
            this.writeCharacters ( Character.toString ( c ) );
            return;
    }
    super.writeEntityRef ( eref );
}

Ceci est équivalent (à part quelques surcharges) au correctif qu'ils ont déjà classé pour ce problème, qui devrait être disponible avec JDK8u192 (et devrait déjà figurer dans JDK 9/10).

2) Enveloppez votre XMLStreamWriter2 avec le filtre ci-dessus, par exemple:

FileOutputStream fout = new FileOutputStream ( "test.xml" );
WstxOutputFactory wsof = (WstxOutputFactory) WstxOutputFactory.newInstance();
XMLStreamWriter2 xmlOut = (XMLStreamWriter2) wsof.createXMLStreamWriter ( fout, CharsetNames.CS_UTF8 );
xmlOut = new NewLineFixWriterFilter ( xmlOut );
// Now write into xmlOut, directly or via JAXB

Le code complet/de production est ici . Il ne devrait pas être difficile d’adapter la même approche à des pipelines similaires (en général, le problème se produit parce que com.Sun.xml.internal.bind.v2.runtime.output.XMLStreamWriterOutput échappe à \n et à \r de manière erronée, l’astuce consiste donc à pirater ce codage incorrect des niveaux supérieurs).

2
zakmck

Geoff S,

J'ai essayé de commenter le post existant, mais j'ai vite découvert qu'il fallait avoir «50 réputations», ce que je n'ai pas.

Il semble que je rencontre un problème similaire lorsque nous avons migré vers les versions 1.8.0_161 et 1.8.0_162 de JDK, certains de nos services SOAP ont commencé à générer les exceptions ci-dessous.

Feb 28, 2018 8:34:12 AM com.Sun.xml.internal.messaging.saaj.soap.SOAPDocumentImpl createEntityReference
SEVERE: SAAJ0543: Entity References are not allowed in SOAP documents
SEVERE: Java.lang.UnsupportedOperationException: Entity References are not allowed in SOAP documents
javax.xml.ws.WebServiceException: Java.lang.UnsupportedOperationException: Entity References are not allowed in SOAP documents
    at com.Sun.xml.internal.ws.handler.ClientSOAPHandlerTube.callHandlersOnRequest(ClientSOAPHandlerTube.Java:135)
    at com.Sun.xml.internal.ws.handler.HandlerTube.processRequest(HandlerTube.Java:112)
    at com.Sun.xml.internal.ws.api.pipe.Fiber.__doRun(Fiber.Java:1121)
    at com.Sun.xml.internal.ws.api.pipe.Fiber._doRun(Fiber.Java:1035)
    at com.Sun.xml.internal.ws.api.pipe.Fiber.doRun(Fiber.Java:1004)
    at com.Sun.xml.internal.ws.api.pipe.Fiber.runSync(Fiber.Java:862)
    at com.Sun.xml.internal.ws.client.Stub.process(Stub.Java:448)
    at com.Sun.xml.internal.ws.client.sei.SEIStub.doProcess(SEIStub.Java:178)
    at com.Sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.Java:93)
    at com.Sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.Java:77)
    at com.Sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.Java:147)
    at com.Sun.proxy.$Proxy38.getUserProfile(Unknown Source)

Comme indiqué par la question ci-dessus et d'autres discussions: 

Cela a quelque chose à voir avec les nouvelles lignes dans la charge utile. Par exemple, certaines de nos charges utiles incluent des chaînes XML comportant de nouvelles lignes à l'origine du problème. Toutefois, si les nouvelles lignes sont supprimées avant d'appeler le service, cela fonctionne. Voir immédiatement ci-dessous:

Échouer

<?xml version="1.0" encoding="UTF-8"?>
<user>
<userId>XXXX</userId>
<name>XXXXXX, XXXXXX</name>
<phone>(xxx)xxx-xxxx</phone>
<title><![CDATA[MY TITLE]]></title>
<mail>[email protected]</mail>
</user>

Travaux

<?xml version="1.0" encoding="UTF-8"?><user><userId>XXXX</userId><name>XXXXXX, XXXXXX</name><phone>(xxx)xxx-xxxx</phone><title><![CDATA[MY TITLE]]></title><mail>[email protected]</mail></user>

Savez-vous ou quelqu'un d’autre qui sait s’il existe une solution de contournement autre que celle consistant à supprimer la charge utile des «nouvelles lignes»? Cela est-il considéré comme un bogue dans le dernier JDK Oracle et existe-t-il des plans pour remédier à ce problème?.

Merci

max

2
Massimo Mirabito