V práci  pro dotahování dat ze serveru na klienta
používáme Web servisy, konkrétně framework CXF, což
je implementace Jax-WS. Jelikož potřebujeme aby
jednotliví klienti byli schopní fungovat i v offline
módu, dotahuje se v jednu chvíli poměrně velké
množství dat. Vyvinul jsem tedy rozhraní na serveru,
naklepal funkcionalitu na klientovi, a spustil
vývojářský test. Pár chyb tam klasicky bylo, ale v
zásadě nic co by stálo za řeč. Celé operace trvá
přibližně hodinu, a nejdřív to vypadalo nadějně.
Aplikace běžela, a nic nenaznačovalo že by se mělo
stát něco nepatřičného. Pak jsem si došel pro kafe, a
při návratu na mě zírala chybová hláška. Začal jsem
pátrat v konzoli kde by mohl být problém, a stack
trace co jsem tam objevil mě opravdu nepotěšil.
Nebudu ho zde uveřejňovat celý, podstatná část je
tato :

com.ctc.wstx.exc.WstxUnexpectedCharException:
Illegal character ((CTRL-CHAR, code 5)).

Nejdřív jsem samozřejmě netušil, kde by mohl být
zakopaný pes, ale strejda Google opět nezklamal.
Chyba byla v tom, že moje Soap zpráva obsahovala znak
pro ,,Ctrl+v”, který je ve specifikaci XML  1.0
zakázaný znak. I kdyby jste mě rozkrájeli, nemám
nejmenší tušení jak se tento znak do mých dat dostal.
Zprvu jsem měl podezření že je špatně nastaveno
kódování u JDBC driveru, ale při kontrole jsem nic
špatného neobjevil.  Přidal jsem si tedy logování
abych zjistil kde přesně chyba je, a zjistil  jsem,
že znak se nachází u jednoho záznamu v ručně psané
poznámce. No, nejspíš bych měl vysvětlit tu ručně
psanou poznámku. Při provozu jí samozřejmě uživatel
zadává do GUI, ale Peťánek jako tvor v zásadě velmi
líný umí samozřejmě použít update nad databází :-).
Je tedy možné, že se tam balast dostal při mých
čachrech s daty, ale ruku do ohně bych za to
samozřejmě nedal.

Uvažoval jsem co s tím tedy udělám. Spoléhat se na
to, že při provozu se do aplikace nahrajou data,
která podobné znaky nebudou obsahovat je v zásadě
možné, a s vysokou pravděpodobností to tak i bude.
Chtěl jsem ale mít absolutní jistotu, a tak jsem
uvažoval, co s tím.V mé aplikaci nemají podobné znaky
žádný význam, a tak jsem se je rozhodl prostě
odstranit. Nejjednodušší, a samozřejmě špatné řešení
je použít metodu replaceAll() ze Stringu následujícím
způsobem :

replaceAll(“\\p{Cntrl}”“”); 

Samozřejmě
otevřeně přiznávám, že přidávat tento kód do všech
setterů na stringové proměnné v DTO objektech mě
příliš neláká. V zásadě se stydím za to, že mě to
vůbec napadlo. Hledal jsem tedy na Google dál. Doufal
jsem že by mohl existovat nějaké prostředek
frameworku CXF, který podobnou šlamastiku řeší,
jelikož dle mého názoru nejsem první, ani poslední,
který toto řešil. Po chvíli jsem narazil na jistý
příspěvek na blogu, který doporučoval v podobných
případech použít tento snippet kódu :

XMLOutputFactory f = new WstxOutputFactory();

f.setProperty(WstxOutputProperties.P_OUTPUT_INVALID_CHAR_HANDLER,new InvalidCharHandler.ReplacingHandler(‘ ‘)),

XMLStreamWriter sw = f.createXMLStreamWriter(...);

Účel je v zásadě velmi prostý, a
to během serializace do XML vyhodit všechny znaky,
které by mohly na klientovi během deserializace
působyt problémy. Poté samozřejmě nebylo problémem
vytvořit konfiguraci pro Spring, který
InvalidCharHandler.ReplacingHandler(”) CXFku
podstrčí. Konfigurace v zásadě vypadá takto :

 <jaxws:endpoint id="kservice"  

                   
implementor="#kostrounService"

                   
address="/call_kostroun" >

                   
<jaxws:properties>

                           
<entry key="javax.xml.stream.XMLOutputFactory"  valueref="xmlOutputFactory" />

                     
</jaxws:properties>      

   
</jaxws:endpoint>

 
<bean id="invalidCharHandler"   class="com.ctc.wstx.api.InvalidCharHandler$ReplacingHandler">

         
<constructor-arg value=" "/>

   
</bean>



   
<bean id="xmlOutputFactory" class="com.ctc.wstx.stax.WstxOutputFactory"/>



   
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">

       
<property name="targetObject">

           
<ref local="xmlOutputFactory" />

       
</property>

       
<property name="targetMethod">

           
<value>setProperty</value>

       
</property>

       
<property name="arguments">

           
<list>

                 
<util:constant static-field="com.ctc.wstx.api.WstxOutputProperties.P_OUTPUT_INVALID_CHAR_HANDLER"/>

                 
<ref bean="invalidCharHandler" />

           
</list>

       
</property>

   
</bean>

Funguje to dobře, chápu že to nezabraňuje aby se
mlíko rozlilo, nýbrž jen to, aby po rozlití bylo zase
utřeno, ale co člověk nadělá.  Budu samozřejmě vděčný
za komentáře, jelikož si myslím že je důležité dostat
zpětnou vazbu, kritiku nevyjímaje.