( SzBlackY | 2015. 07. 15., sze – 23:11 )

Tegyük fel, hogy írsz egy könyvtárat, ami felhasználó-azonosítást végez. És plug-in-rendszerűen cserélhető mögötte az azonosítási réteg, egyszerű "felsorolom a userek a forráskódban" (itt ugye nagyjából csak a kifogytunk-a-memóriából és a kicsapatták-a-220-ból-a-gépet hibalehetőségek vannak) megoldástól felfelé bármeddig - teszem azt egy krb single sign-ont használó SAML mondjuk LDAP back-enddel (a SAML szerver nem érhető el, DNS hiba van, SSL hiba van a kliens és a SAML szerver közt, SSL szerver van a SAML és az LDAP szerver közt, a kliens nincs negotiate-re beállítva a SAML szerverhez, a krb szerver lehalt, az LDAP tanusítványa lejárt, bárhol hálózati hiba van, redundáns rendszereknél kidőlt valamelyik node, de a DNS round robin vagy a load balancer oda küldi, a negotiate auth bárhol elhasalt, time-outolt a session a SAML-en az auth előtt/alatt stb.).

A kliens így azon túl, hogy "nem megy az auth, sorry" sok mindent nem tud kezdeni (mert nem tudja, hogy milyen providerek vannak egyáltalán és nem tudja, hogy melyik provider hány féleképpen tud elhasalni). Egy adminisztrátornak viszont fontos információ lehet, hogy pl. az SSL hiba miatt nem tudott az LDAP-hoz csatlakozni. Itt akkor két lehetőséged van: "helyileg" kezeled ezeket az eseteket, így a hibakezelést (ami így muszáj, hogy kimerüljön a naplózásban) ki kell, hogy merüljön abban, hogy kivésed a logba, hogy gond van, aztán visszaadsz egy random hibakódot, jó esetben hibaüzenettel együtt - amit utána még feljebb adogatsz, vissza a kliens kódig, akkor kiírja, hogy "nem megy az auth, sorry" és REMÉNYKEDIK, hogy a megfelelő modul kivésett bármi használható az error logba. A másik lehetőség, hogy szépen kitalálsz egy (minden valószínűség szerint rekurzív) struktúrát arra, hogy a hibákról a lehető legtöbb információt visszaadj (hiba fajtája, kódja, leírása, paraméterei [melyik szerver melyik portjánál ment szét egy SSL handshake]) - mindezt úgy, hogy gyakorlatilag bármilyen elborult implementációra előre fel kell készülnöd.

Ha ugyanezt kivételekkel oldod meg: ahol felmerül a hiba (ez ugye a plug-in lesz, neki sok hibát nem feladata kezelni) a tényleges hibát (legyen egy SSLException, de ugyanígy lehetne SQLException, ...) becsomagolod egy AuthException-be, és feldobod az auth alrendszernek. Ő ott szépen (contracttől függően) kivési a logba a hibát és - függően az auth és a kliens közti contract-től - vagy továbbdobja a kliensnek vagy visszatér egy false-al.

A kliens kódod nagyjából ugyanúgy fog kinézni, gyakorlatilag szintaxis-beli különbség lesz benne: próbáljunk meg auth-olni, ha bármi gond van, szóljunk a usernek, hogy "bocs, baj van, próbáld később". Az igazi különbség az egyes plug-inek kódjában lesz, akiknek nem kell naplózással foglalkozniuk (mert nem feladata... ha biztosít debug logging, az jó, de a "rendes" naplózás ne az ő felata legyen - lásd jó pár PAM modult, amiknek van egy debug kapcsolója), nem kell nekik újrakitalálniuk a spanyol viaszt (a fentebb emlegetett rekurzív struktúra, amit arra használnánk, hogy egy felmerülő és komoly problémát jelezzünk vele engem nagyon emlékeztet az Exception-ökre), ráadásul ugyanúgy, ahogy így a kliens kódtól egy AuthException-nel többé-kevésbé elabsztraktáltuk a teljes auth alrendszert, a plug-inek kódjától is ugyanígy el lehet választani a teljes pl. fájlrendszert (mert mondjuk ki mondta, hogy a .htpasswd fájlunk helyi fájlrendszeren van, és nem egy pár száz km-re levő adatközpontban egy cluster fájlrendszeren...)

És gyakorlatilag ez a lényeg: azzal, hogy származtatható kivételeket használsz, nyersz két fontos dolgot: mindenképp kényszerítetted a kliens kódodat legalább a hiba típusának a definiálására (*) és gyakorlatilag mindenki elől elrejtetted az összes szart, ami kártyavárként rá dőlhet - és előbb-utóbb rá is fog. Viszont mivel legalább egy hiba típust tudsz, még ha vállalhatatlanul van is megírva az éppen megboruló provider-ed, az admin el tud indulni valahol.

(*) pl. így:


abstract class AuthException extends Exception {}
interface AuthPlugin {
 public boolean auth(...) throws AuthException;
}

Aztán persze mindig lesznek barmok, akik simán dobálnak a plug-in kódjukból unchecked exception-öket, mert hát csak...

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)