Singleton a problém ClassLoaderu
Používáte návrhový vzor Singleton a používáte jej v Jave? Pro ty méně znalé osvětlím, že Singleton je takový
objekt, který má v celé aplikaci pouze jedinou instanci. Používá se tam kde chceme, zaručit
jediněčnost objektu například v implementaci Cache.
Pěkných pár dní jsem pracoval na implementaci XML konfigurace aplikačního serveru,k tomuto účelu jsem
využil projekt Jakarta Apache jehož součástí je komponenta
Commons Digester hojně využívaná v projektech
pod Jakartou.Digester slouží pro namapování XML na javovské objekty.
Digester umožňuje nastavit objekt, který je volán(událostní model SAXu) při zpracování dokumentu. Tímto
mechanismem jsem vždy získal jméno aktuálně zpracovávaného elementu a toto jméno jsem dále využíval v jiném objektu, který
pracoval z obsahem elementu. Měl jsem tedy dva objekty jeden, který vždy zpracoval obsah elementu a jeden, který byl
pověšen na zpracování dokumentu. Objekt, který zpracovával obsahy elementu vždy potřeboval znát jméno aktuálně zpracovávaného
elementu, aby mohl jeho obsah korektně uložit(jméno elementu tvořilo klíč v mapě). Právě objekt, který čmuchal jména elementů
byl Singletonem.
Tento model fungoval dobře v kontextu našeho aplikačního serveru, ovšem pokud jsem zkoušel přenést aplikace
do jiného servletového kontejneru nastala potíž. Najednou nebyl objekt jediněčný, ale vznikaly instance dvě a
to mělo za následek chybnou funkčnost.
Přitom Singleton byl thread-safe, metoda vracející instanci byla synchronizovaná.
public class RuleWatcher{
private static RuleWatcher watcher = null;
private RuleWatcher(){
// soukromý konstruktor
}
public static synchronized Rulewatcher getInstance(){
if(watcher == null){
watcher = new RuleWatcher();
}
return watcher;
}
.
.
.atd.
.
}Tento kód je v pořádku, přesto nemusí zaručit očekávanou funkčnost. Na otázku proč odpovím ClassLoader. Jak Digester
tak většina servletových kontejnerů používa vlastní ClassLoader, pokud tedy vznikne instanceRuleWatcher
v kontextu ClassLoaderu aplikačního serveru a posléze je vytvořen objekt v kontextu jiného ClassLoaderu(v našem případě Digester)
a volána metodagetInstance()je podmínkatrueneboť třídní instancewatcher
je alokována v jiném kontextu a proto vznikne další instance.
Nálsedující zjištění mi trvalo přibližně den, oprava už nebyla složitá. Naštěstí komponenta či spíše objektDigester
umožňuje nastavení ClassLoaderu proto stačila jediná řádka, která nastavila původní ClassLoader a bylo po problémech.
ClassLoader c = Thread.currentThread().getContextClassLoader();
digester.setClassLoader(c);Související články
Související epizody CZ Podcastu
- #3 · Java 6 SE, Vývoj webových aplikací
Dagi a Filemon o novinkách v Javě 6 a webových frameworcích.
- #1 · Vývojová prostředí v Javě
První díl CZ Podcastu se věnuje vývojovým prostředím pro Javu.