Jak instalator portletów w LIferay wyruchał classloader Tomcata
Kilka dni mi to zajęło, ale dopiero lekkie zapalenie gardła połączone z finałem leczenia za pomocą dwóch napojów piwopochodnych Somersby spowodowało, że mam rozwiązanie.
W czym problem leżał
Mamy sobie task polegający na dopisaniu do istniejącego portletu dodatkowego modułu. W dużym skrócie portlet pobiera dane z formularza www, zamienia je na PDFa i wysyła mailem. My mamy dodać webservice, który będzie niejako dodatkowym kanałem komunikacji. Inaczej mówiąc zamiast pieprzyć się z klikaniem formularza na www wyślemy sobie go z appki po WSie. Samo implementowanie WS jako elementu istniejącego portletu w liferay zasługuje na osobny wpis, bo tak pojebanej technologii to tylko wśród phpowców szukać, a i tu wygląda to logiczniej. Dla nas istotne jest to, że w zależnościach naszego rozwiązania pojawia się cglib-2.1_3 i commons-logging-1.0.4.
Objawy problemu
Dewelopujemy, paczkujemy, deployujemy. Na localhoście (goły tomcat) śmiga wyśmienicie. Deploy lokalny wykonujemy jako “rm -rf ; cp” deploy, czyli za pomocą czarnego ekranu, ale wała… deploy na test, odpalamy SoapUI i mamy… InvocationTargetException. Ok… bywa… generalnie ten błąd jest zwracany przez AXISa gdy coś się grubi wyjebie. Niestety w konstruktorze klasy tego błędu mamy:
Listing 1. konstruktor ITE
protected InvocationTargetException() {
super((Throwable)null); // Disallow initCause
}
Wot, bolszoj’ technika. Dla niekumatych… po chuj nam przyczyna błędu. Taki kod podobny jest w swej istocie do pustego bloku catch i powinno się autora na spalić na stosie… hm… stosie… hm… somersbiak się skończył… szit…
Ok, debbuger podpinamy się na wywołanie metody WS i metodą eliminacji dochodzimy do momentu gdzie się wywala. Okazuje się, że przyczyną błędu jest NoClassDefFoundError, który oznacza, że klasa została załadowana, ale w trakcie pierwszego jej wywołania, kiedy następuje jej weryfikacja, niezależenie metody statycznej czy konstruktora, coś nie bangla. I to solidnie nie bangla…
Klasą, która powodowała syf była… springowa ValidationUtils. Chwila zastanowienia… i okazuje się, że rzeczywistą przyczyną był problem z klasami ze wspomnianego wcześniej commons-logging. Tak błąd oznacza najczęściej, że w classpathie leża dwie klasy o identycznej nazwie. Oczywiście szybko się okazało, że rzeczywiście w WEB-INF/lib mam dwie paczki commons-logging, ale tylko na serwerze testowym. Przy okazji okazało się, że zdublowany jest jeszcze cglib tzn. jest jar “pełen” i wersja “nodep”… to nasunęło mi pewną myśl…
Przyczyna problemu
Po kilku krótkich testach znalazłem przyczynę problemu. Mechanizm instalujący portlety w liferayu wykonuje operację kopiowania. Problem pojawia się w momencie gdy z paczki usuwamy jakieś pliki. W trakcie instalacji stara wersja jest nadpisywana przez ekwiwalent bashowej operacji cp, ale nie przez ciąg operacji kasowania i kopiowania. W efekcie jeżeli podbijemy numer wersji jakiejś biblioteki to stara wersja pozostanie na swoim miejscu i będzie bruździć. Zazwyczaj problem ten nie występuje, bo “zabezpieczeniem”, jest “naturalne” działanie użytkowników, którzy ręcznie usuwają stary portlet, a nie zdają się na mechanizmy instalatora. W efekcie nawet gdy wystąpi tego typu awaria następuje paniczne usuwanie portletu z katalogu roboczego i nieświadome gaszenie pożaru. Ja nie zachowałem się jak typowy user, chyba dlatego, że ręczne usunięcie starej wersji gdy instaluje się nową jest dla mnie czymś głupim, tym samym spowodowałem problem, a ze względu na dodatkowo dziwne zachowanie się serwera testowego, ale nie localhosta zacząłem drążyć temat…












January 24th, 2013 at 23:23
Czy byłbyś w stanie/chciałbyś/miałbyś czas/miałbyś chęci napisać kiedyś własny tutorial do Liferaya / portletów? Bo niby sporo w sieci, ale jak chcesz coś zrobić jak najlepiej (czyt. dobre praktyki przy tego typu aplikacjach, czy czasami jak coś zrobić od początku do startu portalu). Podejrzewam, że taki tutorial byłby mile przyjęty, no ale wiadomo – decyzja jest Twoja. Na pewno jest nisza na takie coś.