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

2010-12-06

Użycie widoków w Hibernate

Spieszę donieść, iż błąd uniemożliwiający użycie @SecondaryTable w klasie bazowej, na który utyskiwałem w tym wpisie został w końcu poprawiony (po czterech latach od zgłoszenia!). Uaktualniłem projekt i rzeczywiście Hibernate w wersji 3.5.4 poprawnie obsługuje mapowanie bazowej encji na wiele tabel. Trafiłem jednak na kolejny problem i - w przeciwieństwie do opisywanego wcześniej - nie bardzo widzę sposób na obejście go.

Mając tabele i widok:

CREATE TABLE `TB_SOME_TABLE` (
`ID_PARENT` INT(11) NOT NULL AUTO_INCREMENT,
`READ_WRITE` TEXT,
PRIMARY KEY (`ID_PARENT`)
)

CREATE TABLE `TB_OTHER_TABLE` (
`PARENT_ID` INT(11) NOT NULL,
`SOME_DATA` TEXT
)

CREATE VIEW `VW_SOME_VIEW` AS
SELECT
`T`.`PARENT_ID` AS `PARENT_ID`,
COUNT(`T`.`PARENT_ID`) AS `READ_ONLY`
FROM
`TB_OTHER_TABLE` `T`
GROUP BY
`T`.`PARENT_ID`

i klasę:

@Entity
@Table(name = "TB_SOME_TABLE")
@SecondaryTable(
name = "VW_SOME_VIEW"
pkJoinColumns = @PrimaryKeyJoinColumn(name = "PARENT_ID")
)
public class Parent {
@Id
@Column(name = "ID_PARENT")
private Long id;

@Basic
@Column(name = "READ_WRITE, table = "TB_SOME_TABLE")
private String readWriteAtribute;

@Basic
@Column(
name = "READ_ONLY", table = "VW_SOME_VIEW",
insertable = false, updatable = false
)
private String readOnlyAttribute;

...
}

nie jesteśmy w stanie zapisać do bazy obiektu tej klasy. Pomimo, że jedyny atrybut zamapowany na widok jest tylko do odczytu (insertable i updatable równe false), to Hibernate próbuje wstawić dane do widoku, co kończy się wyjątkiem:
Unhandled exception 'JDBC exception on Hibernate data access:
SQLException for SQL [insert into VW_SOME_VIEW (PARENT_ID) values (?)];
SQL state [HY000]; error code [1471]; could not insert: [Parent]

Widać że się stara (bo w wygenerowanym SQLu nie ma kolumny READ_ONLY), ale do końca mu nie wychodzi (bo jednak próbuje wstawić do widoku wiersz nie zawierający żadnych danych). I zupełnie nie mam pomysłu co na to poradzić.
Share:

2010-10-26

Krótko

Nabyłem drogą kupna Photoshopa (Elements oczywiście). Wiem: ekstrawagancja i burżuazja, ale czasami mi się to zdarza. Ale ja nie o tym chciałem. Rzeczony Photoshop został wysyłany przez Amazon UK DHLem za darmo (od niedawna Free Super Saver Delivery obejmuje też Polskę) i kosztował 280 PLN. W naszym rodzimym sklepie, np. przysłowiowym Vobisie kosztuje 399 PLN. Rrrwa mać po trzykroć.
Share:

2010-10-17

Icon Finder

Nie raz i nie dwa pisząc program lub tworząc stronę internetową potrzebowałem różnego rodzaju ikon. Podstawowe kryteria, które musiały spełniać to: estetyczne, spójne i last but not least legalne. Bardzo ładnym i obszernym zestawem są Crystal Icons (licencja LGPL), ale jednak nie do wszystkiego pasują - są odrobinę zbyt śliczne. Drugi zestaw z którego tu i ówdzie korzystałem to Mini Icons ze strony famfamfam.com - prościutkie, małe ikonki idealne do użycia w aplikacjach webowych, ale jest ich tylko 144 i nie zawsze znajdzie się to czego potrzeba. Dzisiaj trafiłem na stronę Icon Finder. W tej chwili jest tam 645 zestawów, 188325 ikon! I przede wszystkim możliwość wygodnego wyszukiwania bo wszystkie ikonki mają przypisane tagi (np. "red", "arrow", "left"). Mój ulubiony na dzień dzisiejszy zestaw to Simplico (licencja Creative Commons).
Share:

2010-08-26

Groovy 1.7.0 i Spring

W jednej z aplikacji które popełniłem, używam skryptów Groovy jak kontrolerów Spring MVC. Jest to wygodne w sytuacji, kiedy kod kontrolera często się zmienia a nie ma możliwości restartu całej aplikacji. Dzięki poniższej definicji:

komponent some.Controller będzie automatycznie przeładowywany (czyli skrypt będzie ponownie kompilowany) jeżeli plik źródłowy Script.groovy się zmieni (ale nie częściej niż podany czas - tutaj 1000 ms). Wszystko działało tak jak powinno do momentu upgrade'u Springa do wersji 3.0.0.RELEASE - wtedy aplikacja przestała zauważać zmiany w skryptach. Uznałem, że chwilowo mogę bez tego żyć a w wersji 3.0.1 pewnie poprawią. Kiedy jednak po kolejnych uaktualnieniach doszedłem do wersji 3.0.3 i dalej nie było ani trochę lepiej, zacząłem intensywniej szukać. Okazało się, że Spring jest niewinny a problemy sprawia sam Groovy, którego wersję podniosłem do 1.7.0 na fali ogólnego uaktualniania aplikacji (w zależnościach Springa jest 1.6.3). Aby dynamiczne przeładowywanie zaczęło znowu działać trzeba albo cofnąć się do wersji 1.6.x, albo użyć najnowszego Groovy (w tej chwili 1.7.4). Dla zainteresowanych zgłoszenie w JIR-ze GROOVY-3981.
Share: