Dlaczego warto poznać Stripes Framework

Wpis dodany 24 sierpnia 2009 o godz. 20:31:32 w kategorii Java, Spring, Stripes, Techblog.

Frameworków webowych dla Javy jest mnóstwo, a wybór najlepszego to temat na wielodniowy "flame". Wicket jest na topie, w GWT nie trzeba pisać HTML-a, JSF to standard, a Struts 2 to prawie że potomek legendarnych Strutsów. Istnieje jednak framework webowy, który może nie jest aż tak bardzo popularny jak wyżej wymienione, ale warto go poznać, bo w pełni na to zasługuje. Mowa oczywiście o tytułowym Stripes Framework.

Używałem już paru szkieletów webowych, ale przy ostatnim projekcie, w którym decydowałem o wyborze technologii, moje rozważania zakończyły się wyborem tandemu Spring + Stripes. Z perspektywy czasu widzę, że akurat w tym wypadku był to bardzo dobry traf, pomimo braku wcześniejszych doświadczeń ze Stripesami (chociaż parę mądrych osób wcześniej mi mówiło, że ten framework ma właściwie wszystko, czego się wymaga od javowego frameworka webowego - i mieli oni rację).

Nie będę tutaj opisywał, jak napisać "Hello world", bo to już zrobili inni. Napiszę za to, dlaczego lubię Stripesy i dlaczego właśnie je warto użyć zamiast chociażby takiego Struts 2. Nieprzypadkowo wspominam tutaj o S2, gdyż w założeniach są to biblioteki bardzo podobne, oba frameworki są bowiem zdecydowanie akcyjne, nie komponentowe. Niektórym może się ta cecha podobać, innym mniej, ale ponoć "de gustibus non est disputandum", zresztą nie chcemy tu wspomnianego flejma.

Jedną z miłych rzeczy, które spotkamy w Stripes Framework, jest wbudowany bardzo prosty mechanizm do powtórnego wykorzystywania layoutów. Dzięki trzem prostym tagom odpada nam konieczność używania zewnętrznych rozwiązań jak Tiles czy SiteMesh. O ile ten drugi jest przyjemny w użyciu, o tyle ilość xml-a koniecznego do wyprodukowania w Tilesach przy braku konwencji skutecznie do tej biblioteki zniechęca.

Kolejną sympatyczną niespodzianką jest tag do zrobienia kontrolki wyboru z javowych enumów. Standardowo w JSTL czegoś takiego nie znajdziemy, Struts 2 też nie oferuje w tym wypadku jakiegoś gotowego rozwiązania, a jest to chyba dość popularne użycie enumów w aplikacjach internetowych. W Stripesach możemy użyć taga <stripes:options-enumeration/>, a o tym, w jaki sposób to zrobić, można doczytać w dokumentacji.

Na dokładkę zostają nam sprawy konfiguracji. Nie mamy ani kawałka xml-a poza standardowym web.xml, żadnych dodatkowych stripes.xml czy pluginów do obsługi adnotacji bądź włączenia konwencji, jeśli takowe w ogóle istnieją i nam zadziałają (tak, nieco piję tutaj do S2). Wszystko dostajemy od razu, a dzięki adnotacji @UrlBinding bardzo łatwo przyjdzie nam zrobić "ładne" URL-e. Być może są to szczegóły, ale z takich właśnie szczegółów składa się praca programisty. Zresztą pozytywnych niespodzianek jest w Stripes Framework o wiele więcej.

Ok, czy ten framework jest "świętym Graalem" wśród innych rozwiązań? Oczywiście nie, aczkolwiek trudno mi tutaj wytknąć jakiekolwiek jego niedoskonałości. Rzecz jasna nie każdemu mogą odpowiadać jsp (aczkolwiek można użyć Velocity czy FreeMaker) jako szablony, no ale to kwestia gustu. To, co mogłoby być nieco lepiej rozwiązane, to integracja ze Springiem, tzn. użycie go do tworzenia akcji tak, jak to robi Struts 2. Obecnie mamy adnotację @SpringBean (swoją drogą to już chyba jakaś konwencja nazewnicza, w Wickecie nazwa adnotacji do wstrzykiwania beanów Springa jest taka sama), natomiast nie mamy dostępu do np. @Value ze Springa 3. No ale to drobny szczegół (zresztą istnieją projekty, które pozwalają Springowi stać się zarządcą akcji stripesowych, tylko że mają one swoje wady), poza tym w wersji 1.6 (a kiedy piszę te słowa, najnowszą jest 1.5.1) możemy się i rozwiązania tej kwestii spodziewać. W każdym razie polecam przynajmniej spojrzeć przez chwilę na ten projekt, a nuż okaże się on dla Ciebie technologicznym strzałem nie w stopę, lecz w dziesiątkę.

Sposób na JPA i LazyInitializationException

Wpis dodany 17 sierpnia 2009 o godz. 22:37:30 w kategorii Hibernate, Java, JPA, Spring, Techblog.

Temat lazy loadingu (leniwego ładowania?) przewijał się ostatnio na polskich blogach javowych. Pisał o nim Mateusz Zięba, o wydajności takiego rozwiązania wspominał też w świetnym wpisie Sławek Sobótka. Czy można coś jeszcze dodać? Myślę, że niewiele, ale spróbujmy skupić się na jednej rzeczy, a mianowicie na zamkniętej sesji JPA i sposobie, jak sobie w takim wypadku radzić gdy mamy jeszcze w naszych zależnościach Spring Framework oraz działamy w środowisku webowym.

Problem jest dość częsty (przykładowy "stack trace" poniżej) i jako taki ma też rozwiązanie, opisane zresztą w artykule Open Session in View na stronach naszego ulubionego ORM-a. W skrócie całe zamieszanie związane jest z tym, że nie zawsze w naszej warstwie logiki biznesowej operujemy na obiektach, które potem chcemy wyświetlić, a zatem ORM bardzo słusznie nie pobiera ich z bazy danych. Jednak gdy dochodzi do wyświetlenia listy w widoku, zamiast np. listy ulubionych kolorów użytkownika dostajemy błąd numer 500, a w logach straszy nas LazyInitializationException (oczywiście w przypadku H., bo specyfikacja JPA o takiej sytuacji w ogóle nie wspomina). Sesja jest już bowiem zamknięta i nie wiadomo, skąd te kolory wziąć, a całej bazy przecież nie pociągnęliśmy wcześniej, prawda? :). Z przyczyn oczywistych zmiana fetch na FetchType.EAGER raczej nie wchodzi w grę.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: net.miracki.Test.categories, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException (AbstractPersistentCollection.java:380)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected (AbstractPersistentCollection.java:372)
    at org.hibernate.collection.AbstractPersistentCollection.readSize (AbstractPersistentCollection.java:119)
    at org.hibernate.collection.PersistentSet.size (PersistentSet.java:162)
    ...

Problem jest, rozwiązanie dla Hibernate'a też. Niestety niewielki kłopot pojawia się, kiedy chcemy być standardowi i używamy Hibernate (albo czegokolwiek innego) poprzez właśnie JPA. Jak to zazwyczaj bywa, Spring Framework ma na takie zachowanie gotową i prostą receptę. W zasadzie wpis mógłby się kończyć na poniższym kawałku pliku web.xml:

    
        JpaFilter
        org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
    
    
        JpaFilter
        /*
    

Dzięki temu filtrowi nie powinniśmy więcej spotkać wyjątku związanego z "lazy loadingiem". Powyższy filtr istnieje zresztą też w wersji dla zwykłego Hibernate (jako klasa OpenSessionInViewFilter).

Abstrahując już od głównego tematu, ORM-y to narzędzia, które przed użyciem wypada przynajmniej choć trochę poznać, a do tego tutorial jak zrobić "Hello World" nie wystarczy. Brałem udział w projekcie, który z dobrodziejstw opisanych filtrów nie korzystał, przez co niektóre metody w warstwie logiki biznesowej posiadały argumenty typu boolean o nazwach attachCategories, attachCountries itp. Nie wiem, czy byłbym jednak w stanie wymienić wszystkie zasady dobrego kodowania, które w ten sposób zostały złamane ;). Ale jeśli chodzi o ORM-y, to prawie na samej górze mojej prywatnej listy idiotycznych rozwiązań jest adnotacja @Transient dla obiektów połączona z mapowaniem kluczy obcych do Longów z @Column, bo "pociągniesz całą bazę, ORM-y to zło, ale niech już zostaną". Na całe szczęście takie podejście bardzo szybko minęło wraz z lekturą manuala...

Pliki *.properties w Spring Framework 3

Wpis dodany 23 lipca 2009 o godz. 22:11:40 w kategorii Java, Spring, Techblog.

Zdecydowana większość projektów, w których miałem okazję brać udział jako programista, opierała się na Spring Framework. Myślą przewodnią, która przyświecała twórcom tego cuda było przede wszystkim to, aby uprościć te rzeczy, co do których nie ma powodu, aby były skomplikowane. I jedną z takich rzeczy jest na pewno przechowywanie ustawień w plikach *.properties.

I znowu sytuacja z plikami *.properties nie jest w Javie tak prosta, jakby to się mogło na pierwszy rzut oka wydawać. Pomijając już kwestie samego wczytania takiego pliku (na przykład do klasy Properites) zwłaszcza w aplikacji webowej, dochodzi jeszcze kwestia kodowania, istotna z powodu częstego wykorzystywania w/w plików do tłumaczeń. Pomimo tego, że źródła możemy mieć w UTF-8, propertiesy zgodnie ze standardem zawsze kodowane są w ISO 8859-1. Chociaż może być to uciążliwe (ale nie z Eclipse razem z odpowiednim pluginem - polecam!), ma to sporo sensu i gwarantuje, że żaden nowy programista z błędnym kodowaniem projektu nie doda nam śmieci w postaci kwadracików i gwiazdek do repozytorium.

No ale powróćmy do samego Springa. Nie jest nowością wykorzystywanie ustawień z propertiesów w springowym xml-u i wstrzykiwanie ich do beanów. Można to zrobić mniej więcej tak:

<context:property-placeholder location="classpath:settings.properties" />

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.postgresql.Driver" />
    <property name="url" value="${db.url}" /> 
    <property name="username" value="${db.username}" />
    <property name="password" value="${db.password}" />
</bean>

Pewien kłopot zaczyna się, kiedy chcemy pozbyć się wszechobecnego xml-a (a chcemy, prawda?) i używać adnotacji. Do tej pory nie można było wykorzystać beana, który wczytywał propertiesy (jest to zresztą obiekt klasy PropertyPlaceholderConfigurer), aby w stworzonych przez nas ziarnach springowych do takich ustawień prosto się dostać. Sytuacja kończyła się zwykle tym, że powstawał jakiś ConfigurationService, który poza Springiem (bądź też wykorzystując niektóre jego niskopoziomowe mechanizmy) wczytywał dane z pliku (bądź plików) *.properties, a sam był wstrzykiwany wszędzie tam, gdzie potrzebny był dostęp do tychże ustawień.

Można spytać, czy nie da się tego zrobić prościej. Otóż w Spring Framework 3.0 jak najbardziej jest to możliwe dzięki adnotacji @Value. Wystarczy użyć następującej konstrukcji:

    @Value("${admin.email}")
    private String adminEmail;

Pewne zamieszanie może być związane z tym, że jednocześnie w nowej wersji Springa wprowadzono expression language, który również może być wykorzystywany w xml-u czy w adnotacjach. Kawałek kodu, który wkleiłem powyżej z dobrodziejstwa springowego EL nie korzysta, poniższy natomiast już jak najbardziej:

<util:properties id="settings" location="classpath:settings.properties"/>
@Value("#{settings.adminEmail}")
private String adminEmail1;

@Value("#{settings.admin.email}")
private String adminEmail12;

Różnica jest znaczna, chociaż teoretycznie występuje w jednym znaczku, tzn. w #. Jest to jednak zupełnie odmienna konstrukcja, której spore możliwości można dokładniej poznać przeglądając dokumentację. Warto jednak zauważyć, że jedynie zmienna adminEmail1 zostanie ustawiona, o ile w naszym pliku settings.properties znajdziemy wartość dla klucza adminEmail. Do zmiennej adminEmail2 byłaby wstrzyknięta wartość jedynie wtedy, jeśli admin byłoby obiektem z atrybutem email, a wiemy, że tak nie jest.

Jeśli zaś chodzi przykłady, gdzie Spring-EL się naprawdę przydaje, to kolejny raz odsyłam do wpisu o najnowszym Spring Security, warto zobaczyć jakie nowości udostępnia nam kolejna wersja tej biblioteki.