středa 30. června 2004

Sun Multi-Schema XML Validator a kompilace schématu

V projektu, na kterém právě pracuji, potřebujeme validátor XML schémat, který bude sloužit k validaci XML dat na vstupu aplikačního serveru. Řekl jsem si no problem a šel jsem na to. Samozřejmě jsem netušil, že příští den a půl strávím hledáním hloupé chyby.

Moje pouť začala u Sun Multi-Schema XML Validator, který implementuje obecné programové rozhraní JARV určené pro validátory. Diky tomuto rozhraní je možné programově pracovat s validátorem. Více na JARV User's Guide.

JARV mi nabídl ideální možnost, jak uschovat validátor do komponenty, kterou bude využívat aplikační vrstva. Těch několik schémat, která budeme používat, máme uloženy v rámci aplikačního serveru a proto jsem zvolil jejich kompilaci v init metodě validační komponenty.

Kompilace schématu je proces kdy se schéma převádí na objektovou podobu. V chytré příručce je ukázka Schema schema = factory.compileSchema("http://www.example.org/test.xsd");

Převedeno do kontextu aplikačního serveru, schéma mám jako resource(*.xsd soubory) webové aplikace. Předběžná kontrola API objektu VerifierFactory a metody compileSchema, vybírám compileSchema(java.io.InputStream stream).

Výše uvedené dalo vzniknout následujícímu kódu (pouze to nejdůležitější)

 
   factory = VerifierFactory.newInstance("http://www.w3.org/2001/XMLSchema");              
   .
   .
   InputStream is = context.getResource("foo.xsd").getInputStream();
   Schema schema = factory.compileSchema(is);
 

Připravuji testovací objekt, který má validační komponentu vyzkoušet. Pouštím test a ejhle inicializační metoda, kde je výše uvedený kód, vylítne na výjimku. Letmý pohled na stack trace, kde je text, který se mi na den a půl vryje do paměti

com.sun.msv.verifier.jarv.FactoryImpl$WrapperException: java.lang.NullPointerException (celý stack trace)

Po dni prošpikovaném debuggováním, hledáním alespoň nějaké stopy via. Google a zkoušením všeho možného a nemožného docházím k závěru, že to postě nejde a přemýšlím kde udělali soudruzi z NDR chybu. Procházím ještě jednou příklady a říkám si, safra nějak to přece musí jít a zkouším po vzoru několika příkladů podstrkovat místo InputStream přímo File compileSchema(java.io.File f) a WOW ono to funguje!

To není možné, přece nemají chyby v metodě compileSchema(java.io.InputStream stream) snažím se sám sebe přesvědčit a koukám ještě do API objektu VerifierFactory a vidím něco čeho jsem si nevšiml, dvě metody s InputStreamem, compileSchema(java.io.InputStream stream) a compileSchema(java.io.InputStream stream, java.lang.String systemId). Ta druhá má jako argument systemId (systémová identifikátor schématu např. foo.xsd), hloubám v paměti na co to padalo... ic.systemId.equals(url) v objektu com.sun.msv.reader.GrammarReader metoda switchSource( InputSource source, State newState ).

Teď už mi to dochází, ic je InclusionContext privátní třída v com.sun.msv.reader.GrammarReader, to jsem již vyzkoumal při pročítání kódu GrammarReader. Sepnuto, systemId na ic bylo null (to jsem již věděl) a ten důvod byl prostý.

InclusionContext (jak jsem vyzkoumal) vzniká skrze compileSchema a protože některá schémata mají externí entity např. import typů vznikne i odpovídající InclusionContext pro tyto entity. Chudák validátor se pak snaží InclusionContext identifikovat podle jejich systemId viz. ic.systemId.equals(url) a protože jsem použil compileSchema(java.io.InputStream stream) bez systemId s odkazem na externí entitu, tak to skončilo tím NullPointerem. Pokud bych měl zrovna štěstí a předhodil soubor bez externích entit, tak by vše proběhlo v pořádku neboť by vznikl pouze jeden InclusionContext.

Stačila málá úprava kódu a vše běželo podle očekávání

 
   factory = VerifierFactory.newInstance("http://www.w3.org/2001/XMLSchema");              
   .
   .
   InputStream is = context.getResource("foo.xsd").getInputStream();
   Schema schema = factory.compileSchema(is,"foo.xsd");
 

Z výše uvedeného plyne, hned několik ponaučení a chyb. Za prvé, dokumentace (JavaDoc) k rozhraní JARV je nedostatečná. V JavaDocu neni o něčem takovém ani zmínka, všechny metody compileSchema mají stejný dokumentační komentář, nelze tedy určit která by se měla v tom či onom případě použít.

Opět se mi potvrdilo, že ti co hlásají, že dostupnost zdroj. kódu je k ničemu, nemají pravdu. Bez zdrojáků bych byl, nejen v tomhle případě, ztracen nebo v lepší variantě zasekán několik dalších hodin.