20 maja 2007

HTML na stronach JSP dla technologii JSF czyli biblioteka HtmlLib

JSF zakłada z goła inny model tworzenia interfejsu użytkownika niż ten znany z technologii takich jak Servlety czy JSP. Co prawda tworząc strony JSF typowo posługujemy się technologią JSP, ale jest to już inne podejście. Tworzenie strony DHTML, bo ostatecznie wszystko się do tego sprowadza, nie ma już być mozolnym rzeźbieniem. JSF to komponenty a JSP jest tylko pewnym sposobem zdefiniowania ich wzajemnego zawierania i atrybutów. Jak to jednak bywa z nowymi pomysłami i technologiami ciężko jest się do nich przyzwyczaić, przestawić swój tok myślenia i zarzucić wszystko to co choć stare i słabe to jednak dobrze znane. Nade wszystko, te nowe technologie miewają także wady, w pewnych zastosowaniach są po prostu gorsze. Ale do rzeczy. Stanąłem dziś przed problemem stworzenia strony JSP dla JSF takiej, która wyświetlałaby obiekt currentItem z zastosowaniem następującego formatowania:

currentItem.name

currentItem.picture

currentItem.description


Stosując stary dobry HTML na starej dobrej stronie JSP użyłbym tabeli. Mogłoby to wyglądać jakoś tak:

<table>
<tr>
<td>
${currentItem.name}
</td>
<td rowspan=”2”>
${currentItem.picture}
</td>
</tr>
<tr>
<td>
${currentItem.description}
</td>
</tr>
</table>

No dobrze, ale my z wielu powodów nie możemy użyć wprost elementów HTML. Przynajmniej nie zawsze. Z chęcią poznam zgrabne rozwiązanie powyższego problemu przy użyciu standardowych komponentów JSF, a póki co zaproponuje co innego. Rozwiązania standardowego nie poszukiwałem długo i wytrwale, bo bardzo szybko znalazłem rozwiązanie polegające na użyciu biblioteki HtmlLib. Mała rzecz a cieszy. Jest to po prostu implementacja HTML dla JSF. Biblioteka ta zawiera znaczniki HTML zaimplementowane jako komponenty JSF. Wystarczy pobrać plik htmlLib.jar ze strony http://jsftutorials.net/htmLib/, dodać go do bibliotek projektu i gotowe. Deklaracja biblioteki znaczników na stronie JSP wygląda następująco:

<%@ taglib uri="http://jsftutorials.net/htmLib" prefix="htm" %>

Możemy teraz używać wszystkich elementów standardowego HTML’a, wystarczy poprzedzić je zakresem przestrzeni nazw htm. Rozwiązanie powyższego problemu, w pełni zgodne z JSF będzie więc wyglądać następująco:

<htm:table>
<htm:tr>
<htm:td>
<h:outputText value="#{currentItem.name}" />
</htm:td>
<htm:td rowspan="2">
<h:outputText value="#{currentItem.picture}" />
</htm:td>
</htm:tr>
<htm:tr>
<htm:td>
<h:outputText value="#{product.productDescr}" />
</htm:td>
</htm:tr>
</htm:table>

13 maja 2007

Repozytorium plików SVN z klientem TortoiseSVN na Windows

Kiedyś gdzieś na jakiejś prezentacji widziałem bardzo ciekawą i nadzwyczaj trafną definicję słowa Informatyka. Dokładnej jej treści nie udało mi się odszukać, ale brzmiało to jakoś tak: Informatyka to ogół dyscyplin naukowych zajmujących się informacją, a w szczególności jej komputerowym przetwarzaniem. I w rzeczy samej, najistotniejsze są dane a cała reszta to tylko ich pewne przetwarzanie, w szczególności gromadzenie i prezentacja w wygodnej formie. Jako że temat dzisiejszego artykułu jest dość „niedzielny” pozwolę sobie na zwiększoną porcję dywagacji. No więc jak można przetłumaczyć słowo Informatyka na język angielski? W powszechnym obiegu równie często spotyka się dwie możliwości: „Computer Science” i „Information Technology”. Które moim zdaniem jest właściwsze? To zależy. Ci, którzy w Informatyce widzą więcej komputera powinni wybrać „Computer Science”. Ci, dla których podobnie jak dla mnie jest to raczej nauka o przetwarzaniu informacji, w typowej sytuacji przy pomocy komputera, powinni skłonić się ku drugiej z możliwości.

Moje informacje, dane, bywa czasem, że są dla mnie nadzwyczaj cenne. Nadzwyczaj cenne są dla mnie dane związane z moją pracą magisterską, w szczególności jej tekst i kod źródłowy aplikacji. Nie wiem czy jest to przypadłość cechująca większość ludzi czy tylko tą pedantyczną część, w każdym razie zaobserwowałem, nie tylko na samym sobie, istnienie syndromu przedwczesnego kasowania. Wydaje się nam chwilowo, że pewien fragment kodu czy tekstu jest zły, więc zastępujemy go nowym, a stary oczywiście z miejsca kasujemy i często niedługo po tym tego żałujemy. Kto nigdy w życiu nie użył Windows’owego kosza, czy DOS’owej lub Linux’owej komendy undelete niechaj pierwszy rzuci kamień. Pomijam tych, którzy nie używali analogicznego mechanizmu, bo go po prostu nie mieli. To, co proponuję i to, co sam robię, to używanie do składowania naszych ważnych danych repozytorium plików Subversion, w skrócie i pewnie dla analogii do starszego i gorszego, ale dobrze znanego CVS nazywanego także SVN. Ideałem byłoby używanie repozytorium rezydującego na jakimś zdalnym serwerze, ale przy braku takiej możliwości dobrze będzie mieć to nawet lokalnie. No więc zaczynamy instalację.

Zaczynamy od serwera Subversion. Ze strony projektu, http://subversion.tigris.org/, pobieramy program instalacyjny. W chwili pisania tego artykułu najnowsza wersja to 1.4.3. Instaluje się typowo dla Windows, klikając kilkukrotnie guzik Next. Przystępujemy do instalacji klienta TortoiseSVN. Instalator pobieramy ze strony projektu http://tortoisesvn.tigris.org/. Do Subversion 1.4.3 pasuje TortoiseSVN w wersji 1.4.3 oraz 1.4.2, ale chyba nie ma powodu by używać starszej wersji. Instalacja znowu przebiega typowo dla Windows. Zanim przejdziemy do konfiguracji trzeba będzie zrestartować komputer.

Zaczynamy od utworzenia katalogu, który będzie zawierał wszystkie nasze repozytoria. Standardowym katalogiem instalacji SVN jest C:\Program Files\Subversion. Proponuję utworzyć podkatalog repos w tym właśnie katalogu, ale nie ma żadnych ograniczeń co do nazwy czy też lokalizacji. W katalogu repos tworzymy teraz podkatalog, który będzie zawierał nasze repozytorium. Nazwijmy go testowe. Aby zrobić z tego zwykłego katalogu repozytorium klikamy na nim prawym guzikiem myszy i z menu kontekstowego wybieramy opcję TortoiseSVN > Create repository here, tak jak pokazano na poniższej ilustracji. Wybieramy jeden z dwu typów repozytorium. Oba będą równie dobre.


Musimy teraz skonfigurować zasady kontroli dostępu. W tym celu edytujemy plik testowe\conf\svnserve.conf. W najprostszym przypadku możemy po prostu wyłączyć uwierzytelnianie poprzez odkomentowanie linii:
anon-access = read
auth-access = write
i zmianę wartości read na write dla parametru anon-access. Plik svnserve.conf jest dobrze samoudokumentowany i powstrzymam się od tłumaczenia.

Środowisko mamy gotowe. Trzeba jeszcze tylko uruchomić serwer ze wskazaniem na katalog zawierający repozytoria. Serwer konfigurujemy jako usługę uruchamianą automatycznie przy starcie systemu operacyjnego. W tym celu z linii komend odpalamy polecenie:
sc create svn
binpath= "C:\Program Files\Subversion\bin\svnserve.exe
--service -r \"C:\Program Files\Subversion\repos\""
start= auto
oczywiście zmieniając odpowiednio, w razie potrzeby, ścieżki dostępów. Musimy także uważać na odstępy dookoła znaku =, przed = nie może być żadnej spacji, po musi być dokładnie jedna. Gotowe! Aby utworzyć kopię roboczą (ang. working copy) repozytorium tworzymy pusty katalog o dowolnej nazwie, klikamy na nim prawym guzikiem myszy i z menu kontekstowego wybieramy SVN Checkout. Jako URL repozytorium podajemy svn://localhost/testowe, gdzie testowe to nazwa naszego repozytorium. Musimy tylko pamiętać, że od tej pory nie jest to już zwykły katalog i że do wykonywania operacji na zawartych w nim plikach musimy używać opcji z menu TortoiseSVN (albo innego klienta SVN). Do repozytorium możemy podłączyć się także z poziomu Eclipse z użyciem jednej z dwu wtyczek (ang. plugin): Subversive lub Subclipse. Ta pierwsza jest bardziej rozbudowana funkcjonalnie, ale za to zdarza się, że coś nie działa jak trzeba. W najprostszym przypadku lepsza będzie ta druga. Wtyczkę do NetBeans’a można pobrać ze strony http://subversion.netbeans.org/.

Ostatnim ogniwem w łańcuchu bezpieczeństwa naszych danych są kopie zapasowe repozytorium. Od biedy, można by zwyczajnie kopiować katalog repozytorium, ale nie jest to z wielu względów dobre rozwiązanie. Szczęśliwie SVN dostarcza bardzo prostego mechanizmu. Aby utworzyć zrzut danych repozytorium wystarczy uruchomić z linii poleceń komendę:
svnadmin dump
"C:\Program Files\Subversion\repos\testowe"
> "C:\Program Files\Subversion\backup\testowe.dump"
i nie trzeba nawet w tym celu wyłączać serwera. Jak można się domyślić spowoduje to utworzenie pliku testowe.dump zawierającego kompletne dane repozytorium. Możliwe jest także tworzenie kopii przyrostowych. Plik kopii zapasowej będzie zawierał wówczas tylko wybrany zakres rewizji. Aby odtworzyć dane z tak utworzonego pliku kopii uruchamiamy polecenie:
svnadmin load
"C:\Program Files\Subversion\repos\testowe"
< "C:\Program Files\Subversion\backup\testowe.dump"
Bardzo dobrym źródłem wiedzy o Subversion jak i o repozytoriach plików w ogólności jest książka dostępna nie odpłatnie pod adresem http://svnbook.red-bean.com/.

25 kwietnia 2007

Co to jest JDO i co nowego w JDO 2.0

JDO, czyli Java Data Objects to standard rozwijany w ramach JCP (akr. Java Community Process). Pierwotna wersja specyfikacji, JDO 1.0, powstała w wyniku prac nad JSR 12 (akr. Java Specification Request) i oparta była o standard ODMG 3.0 (akr. Object Data Management Group). Jej finalna wersja została zatwierdzona i opublikowana w kwietniu 2002. Aktualną wersję JDO, JDO 2.0, opracowano w ramach JSR 243 i zatwierdzono ostatecznie, po długotrwałych trudnościach natury politycznej, w maju 2006.

JDO jest standardem interfejsu programistycznego (ang. API) dla technologii trwałości danych. Główne idee, jakie stara się realizować to pełna niezależność od rodzaju źródła danych oraz przeźroczystość. Niezależność od rodzaju źródła danych oznacza, że aplikacja używająca JDO może być bez żadnych zmian w kodzie uruchamiana na relacyjnej, obiektowej oraz każdej innej bazie danych, a także na źródłach danych, których zwyczajowo nie nazywa się bazami danych. Przeźroczystość przejawia się w kilku aspektach, najważniejsze z nich to:
  • Trwałość przez osiągalność (ang. persistence by reachability) – utrwalając obiekt, który ma referencje do innych obiektów obiekty osiągalne z obiektu utrwalanego explicite zostaną także utrwalone. Jeśli np. nasza klasa Osoba ma referencję nazwaną adresZamieszkania do obiektu typu Adres to utrwalając obiekt klasy Osoba, powiązany z nim obiekt klasy Adres zostanie także automatycznie utrwalony.
  • Przeźroczysta nawigacja – pobierając pewien obiekt ze źródła danych nie pobieramy wszystkich obiektów, które są osiągalne z danego obiektu poprzez referencje. Wyobraźmy sobie obiekt klasy Sprzedawca, który ma referencje do wszystkich obsługiwanych przez siebie obiektów Klientów, którzy z kolei mają referencje do wszystkich swoich obiektów Zamówień, te mają dalsze referencje do obiektów klasy Towar. Byłoby to tysiące lub dowolnie dużo więcej obiektów do pobrania, podczas gdy my chcemy znać tylko imię i nazwisko danego sprzedawcy. Przeźroczysta nawigacja oznacza, że efektywnie pobierany jest tylko obiekt Sprzedawca, zaś dopiero odwołanie się do jego referencji powoduje rzeczywiste, ale zupełnie nie widoczne dla programisty, pobranie obiektu referowanego. Programujemy więc tak jakby wszystkie obiekty były pobrane od razu, ale rzeczywisty odczyt ze źródła danych następuje dopiero wtedy gdy jest to konieczne. Funkcjonalność ta dostępna jest jednak jedynie wtedy, gdy kod wykonuje się w kontekście zarządzanym przez managera JDO.
JDO jest jednak tylko standardem definiującym interfejs programistyczny. Aby mieć z niego jakikolwiek rzeczywisty pożytek niezbędna jest jeszcze implementacja. JDO realizuje ideę niezależności od rodzaju źródła danych tylko w tym sensie, że nie narzuca mechanizmów charakterystycznych dla np. relacyjnych baz danych, rzeczywista niezależność zależy od faktycznego istnienia odpowiedniej implementacji. Referencyjną implementacją dla JDO jest JPOX, http://www.jpox.org/. JPOX 1.0 implementuje JDO 1.0 dla relacyjnych baz danych, JPOX 1.1 to już JDO 2.0 ale również tylko dla relacyjnych baz danych. JDO 2.0 jest w pełni wstecz kompatybilne tak więc implementacja JDO 2.0 jest również implementacją JDO 1.0. JPOX 1.2 dodaje wsparcie dla obiektowej bazy danych DB4O, choć w chwili pisania tego artykułu nie ma jeszcze wersji stabilnej, najnowszą jest JPOX 1.2.0-beta-2. Spis dostępnych implementacji, choć nie wiem czy kompletny, można znaleźć na stronie http://db.apache.org/jdo/impls.html.

Jeśli mamy już w ręce pewien obiekt pobrany ze źródła danych, to kolejne obiekty pobieramy w sposób przeźroczysty, nie widoczny dla programisty, poprzez odwołanie się do referencji tego obiektu w zwykły, charakterystyczny dla Javy a nie dla JDO sposób. Jednak musimy jakoś pobrać ten pierwszy obiekt. Nie owijając w bawełnę. Praca z danymi wymaga istnienia dobrego mechanizmu wyszukiwania. JDO definiuje swój własny język zapytań, JDOQL. Zanim jednak popadniemy w zniechęcenie... JDOQL jest prosty, intuicyjny i... podobny do Javy. Tak! Do Javy, nie do SQL! Począwszy od JDO 2.0 JDOQL został rozszerzony w ten sposób, że można go używać także w bardziej SQL’owy sposób, ale nie trzeba, a udostępniono tą możliwość dlatego, że niektórzy tak wolą, oraz pewnie po to, żeby upodobnić JDO do JPA, ale to inna historia. Stworzenie prostego zapytania mogłoby wyglądać tak:

Query query = pManager.newQuery(Osoba.class, „wiek >= 50 && wiek < 60");

Jak duża jest różnica między JDO 1.0 a JDO 2.0? Ogromna! A właściwie to nie jest to różnica tylko różnice. Niektóre, istotne rozszerzenia, jakie wprowadza JDO 2.0 to:
  • Standaryzacja mapowania obiektowo-relacyjnego – mimo że JDO nie jest w żaden sposób związane z relacyjnymi bazami danych, to w obliczu przytłaczającej dominacji RDBMS’ów jako źródeł danych zdecydowano się na standaryzację mapowania obiektowo-relacyjnego. Mapowanie to definiujemy w pliku XML, no i oczywiście nie musimy tego robić jeśli nie będziemy uruchamiać aplikacji na relacyjnej bazie danych.
  • Rozszerzenie mechanizmu uruchamiania zapytań i języka zapytań JDOQL – liczne usprawnienia, w szczególności, umożliwienie wykonywania zapytań agregujących, np. zliczających liczbę obiektów lub wyliczających sumy, minima czy maksima. Dodanie mechanizmu projekcji, tak by możliwe było pobieranie tylko wybranych atrybutów obiektów, w tym możliwość projekcji do obiektów dowolnego typu. Dodanie mechanizmu stronicowania wyników zapytań.
  • Mechanizm odłączania i przyłączania do kontekstu zarządzanego JDO – wydaje się to być niezbędne dla sprawnej implementacji nowoczesnych aplikacji wielowarstwowych i trudno mi jest wyobrazić sobie, że mogło tego pierwotnie nie być. Obiekt pozyskany ze źródła danych można teraz wyłączyć z kontekstu zarządzania JDO, zmodyfikować w innej warstwie i ponownie przyłączyć, co spowoduje zapisanie jego zmodyfikowanego stanu do bazy danych.
  • Mechanizm definiowania zachłannego pobierania obiektów – mechanizm, który pozwala na zdefiniowanie, które obiekty powiązane referencjami z pobieranym obiektem zostaną pobrane w sposób zachłanny (ang. eager). Np. jeśli obiekt klasy Osoba posiada referencję do obiektu klasy Adres to możemy określić aby pobierając obiekt klasy Osoba pobrać od razu skojarzony z nim adres.
  • Interfejsy zarządzania pamięcią podręczną drugiego poziomu – ze względów wydajnościowych wiele implementacji JDO 1.0 dostarczało pamięć podręczną (ang. cache) drugiego poziomu, tj. współdzieloną przez wszystkie obiekty managerów JDO. JDO 2.0 standaryzuje interfejs programistyczny do zarządzania tą pamięcią podręczną, choć specyfikacja dopuszcza brak rzeczywistej implementacji, tj. implementację w postaci zaślepki.

16 kwietnia 2007

Implementacja metody toString() z użyciem Reflection API

Zapewne każdy przedefiniowywał kiedyś metodę toString() klas dziedziny aplikacji, aby ułatwić sobie testowanie czy wykrywanie błędów. Jest to zajęcie o tyleż proste, co jeśli wykonywane w najprostszy z możliwych sposobów a więc dla każdej z klas z osobna, nudne i nużące. Tym bardziej, że nie ma uzasadnienia dla wykonywania tej pracy. Z pomocą przychodzi Reflection API. Dla tych, którzy posiadają największą z cnót programisty, lenistwo, załączam gotowy do użycia kod. Wymaga on Javy 5, ale łatwo przerobić go tak by działał także na starszych wersjach.

import java.lang.reflect.Field;

public class SelfDescribingObject {
public String toString() {
StringBuffer objDescBuffer = new StringBuffer(this.getClass().getSimpleName());

Class currentClass = this.getClass();
while (currentClass != null) {
Field fields[] = currentClass.getDeclaredFields();

try {
objDescBuffer.append(fieldsToString(fields));
} catch (Exception exc) {
return exc.toString();
}

currentClass = currentClass.getSuperclass();
}

return objDescBuffer.toString();
}

private StringBuffer fieldsToString(Field[] fields) throws IllegalAccessException {
StringBuffer objDescBuffer = new StringBuffer();

for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);

objDescBuffer.append(" ");
objDescBuffer.append(fields[i].getName());
objDescBuffer.append(": ");
objDescBuffer.append(fields[i].get(this));
}

return objDescBuffer;
}
}