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:



... 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:



... by wszystko wróciło do normy. Spring wczytuje wtedy plik za pośrednictwem ServletContextu a nie ClassLoadera i dostaje zaktualizowaną wersję. Howgh.

0 komentarze: