dagblog.
← Archiv

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 instance RuleWatcher
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 metoda getInstance() je podmínka true neboť třídní instance watcher
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 objekt Digester
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);

Verze na Medium

Související články

Související epizody CZ Podcastu