2011-05-17

Jeszcze raz Groovy i Spring

Jakiś czas temu miałem problem z dynamiczną rekompilacją komponentów Spring napisanych w Groovy. Wtedy przyczyną był błąd w samym Groovy, szczegóły opisałem tutaj. Po aktualizacji Groovy wszystko było ok, ale niedawno stwierdziłem że mechanizm dynamicznego przeładowywania znowu przestał działać. Ponieważ w międzyczasie wykonałem spory refactoring aplikacji, to nie bardzo wiedziałem gdzie zacząć szukać sprawcy. Uznałem, że tym razem to nie Groovy - skoro raz zrobili błąd i go poprawili, to szansa na to że znowu się pojawił jest niewielka. Po włączeniu logów na poziomie DEBUG stwierdziłem, że Spring też jest niewinny - gdy plik źródłowy Groovy był zmieniany, w logach pojawiał się spodziewany komunikat: Target refreshed successfully ... czyli skrypt był ładowany od nowa. Pomimo tego w aplikacji dalej używana była pierwotna wersja. Nie będę zanudzał Czytelnika szczegółowym opisem trudności i niebezpieczeństw, którym musiałem stawić czoła aby odkryć istotę problemu i przejdę od razu do clou tego posta. Tym razem bruździł Tomcat, a dokładnie jego ClassLoader.
Jeżeli skrypt zostanie umieszczony w aplikacji webowej w katalogu WEB-INF/classes i będziemy się do niego odwoływać w Springu tak:
<lang:defaults refresh-check-delay="1000"/>
<lang:groovy id="myBean" source="classpath:Bean.groovy"/>
... to po pierwszym wczytaniu Tomcat umieści plik Bean.groovy w cache i do restartu go nie ruszy. Co ciekawe Spring potrafi zauważyć zmianę daty ostatniej modyfikacji pliku i próbuje go ponownie wczytać, ale ClassLoader cały czas zwraca pierwotnie wczytaną wersję. Wystarczy przenieść plik do WEB-INF i zmienić wpis w konfiguracji na:
<lang:defaults refresh-check-delay="1000"/>
<lang:groovy id="myBean" source="/WEB-INF/Bean.groovy"/>
... by wszystko wróciło do normy. Spring wczytuje wtedy plik za pośrednictwem ServletContextu a nie ClassLoadera i dostaje zaktualizowaną wersję. Howgh.
© The Useful Pot To Keep Things In
Maira Gall