15 lutego 2009

Dokończenie rozdziału 4. o kontrolerach z "The Definitive Guide to Grails, Second Edition"

0 komentarzy
Nie chciałem dzielić kolejnego rozdziału 5. o widokach w Grails, więc dzisiaj będzie krótko - dokończenie rozdziału 4. "Understanding Controllers" o kontrolerach grailsowych z książki "The Definitive Guide to Grails, Second Edition".

Uwaga: "krótko" nie musi oznaczać tego, co wielu z Wam kojarzy się z "krótko" ;-)

W Grails mamy do dyspozycji mechanizm przechwytywania wywołania metod znany z programowania aspektowego (ang. AOP - Aspect-Oriented Programming). Grails jest jedynie zawężeniem dostępnych możliwości AOP i udostępnia "rozszerzenia" (ang. advice) przed (before), po (after) oraz wokół (around) wykonania metod(y). Spełnia to jednak podstawowe potrzeby przechwytywania wywołań akcji kontrolerów w Grails.

Deklaracja metody przechwytującej "przed" sprowadza się do stworzenia atrybutu beforeInterceptor w postaci domknięcia w ramach danego kontrolera, np. (Listing 4-37):
 def beforeInterceptor = {
log.trace("Przechwyciłem wykonanie metody $actionName z parametrami $params")
}
Mamy możliwość bardziej precyzyjnego określenia, kiedy i która akcja ma być przechwycona z literałem mapowym, np. (Listing 4-38):
 class AlbumController {
private trackCountry = {
...
}

def beforeInterceptor = [action:trackCountry, only:"show"]
}
Parametr action określa akcję do wykonania, a drugi - only lub except - wskazują, której akcji kontrolera dotyczy.

Zwrócenie false przez metodę przechwytującą wstrzymuje wykonanie docelowej akcji kontrolera.

Deklaracja metody przechwytującej "po" sprowadza się do deklaracji atrybutu afterInterceptor, której wartością jest domknięcie do wykonania. Parametr wejściowy model domknięcia to model wynikowy z akcji, np. (Listing 4-40):
 def afterInterceptor = {model ->
log.trace("Wykonano akcję $actionName, która zwróciła model $model")
}
Grails udostępnia specjalną klasę testową ControllerUnitTestCase do testowania kontrolerów, w której znajdziemy "zaślepki" (imitacje obiektów, ang. mocks) otaczających kontroler obiektów z Servlet API, np. HttpServletRequest oraz metod render i redirect.

Utworzenie klasy testowej kontrolera to grails create-unit-test <klasa-kontrolera>. Klasa, której nazwa kończy się na Tests, powstanie w katalogu test/unit.

Klasa bazowa ControllerUnitTestCase rozszerza GrailsUnitTestCase, która udostępnia metody pomocnicze z imitacją działania klas dziedzinowych i kontrolerów, np. mockDomain, która przesłania dostęp do bazy danych (Listing 4-42):
 void testList() {
mockDomain(Album, [new Album(title: "Tytuł"),
new Album(title: "Inny tytuł")])
def model = controller.list()
assertEquals 2, model.albumList.size()
}
Za pomocą mockDomain przesłoniliśmy dostęp do bazy danych i wykonanie list() zwróciło (a przynajmniej powinno!) wyłącznie 2 albumy. mockDomain ustanawia aktualne dane do skontruowania modelu i wszystkie metody pracują wyłącznie na nich.

Klasa MockHttpServletResponse pozwala na analizę stanu bieżącej odpowiedzi kontrolera. W szczególności, metoda getContentAsString() zwraca treść odpowiedzi, np. (Listing 4-43):
 void testIndex() {
controller.index()
assertEquals "Welcome...", controller.response.contentAsString
}
W powyższym przykładzie, jeśli zadaniem akcji index() jest wypisanie (np. przez render) tekstu "Welcome..." obiekt controller.response z pomocą metody contentAsString pomoże nam w sprawdzeniu tego.

Jeśli w akcji korzystamy z bardziej zaawansowanej postaci render, np. render(view: "show", model:[album:Album.get(params.id)]), wtedy sprawdzenie tego to skorzystanie z kolejnej imitacji (zaślepki) - renderArgs, np.
 mockParams.id = 1
controller.show()
assertEquals "show", renderArgs.view
assertEquals 1, renderArgs.model.album.id
Użyliśmy również mockParams, który imituje obiekt params. Z jego pomocą możemy wypełnić do właściwymi danymi przez wykonaniem akcji kontrolera.

Mamy do dyspozycji imitacje - mockRequest (typu org.springframework.mock.web.MockHttpServletRequest), mockResponse (typu org.springframework.mock.web.MockHttpServletResponse), mockSession (typu org.springframework.mock.web.MockHttpSession), mockParams oraz mockFlash.

Wskazanie początkowego (domyślnego) kontrolera przy wejściu do aplikacji to zmiana pliku grails-app/conf/UrlMappings.groovy, w którym deklarujemy statyczny atrybut mappings, np. (listing 4-45):
 class UrlMappings {
static mappings = {
"/"(controller: "store")
}
}
Wskazujemy jedynie początkowy kontroler, a nie konkretną akcję (jej wyznaczenie to reguły opisane w Rozdział 4. "Understanding Controllers" z "The Definitive Guide to Grails, Second Edition" - zazwyczaj index).

W zasadzie oczywiste (ale mnie początkowo zaskoczyła ta oczywistość) - pusta akcja w kontrolerze sprowadza się do delegowanie wykonania do widoku w grails-app/views/<kontroler>/<akcja>.gsp.

Kolejne strony to materiał o pisaniu testów jednostkowych z imitacjami obiektów w Grails. Warto pamiętać, że "you can never write too many tests!" (strona 101). Z Grails pisanie testów staje się trywialne. Radek Holewa wyraził również podobny entuzjazm w swoim komentarzu do wpisu Inni klienci aplikacji w Grails - rozdział 13 z "Beginning Groovy and Grails":

Co do samego Groovy'ego to używam go obecnie jako język w którym piszę testy jednostkowe w projektach w których biorę udział. Sprawdza się tam idealnie!

Na koniec wzmianka o klasach polecenia, gdzie wypełnienie danymi obiektu grailsowego bez konieczności zapisu do bazy danych to właśnie zadanie dla niego. Równie oczywista, jak poprzednia oczywistość, a wciąż mnie zaskakuje.

Natknąłem się również na konstrukcje znane mi z wcześniejszego programowania w C/C++, tj. (Listing 4-57):
 class LoginCommand {
String login
private u

User getUser() {
if (!u && login) {
...
}
}
...
}
Znaczy się, że null to w Groovy równoznaczne z false! Myślałem, że mam to już za sobą, a tu powrót do korzeni.

Możemy korzystać z pomocniczej metody MockUtils.prepareForConstraintsTests(), która akceptuje klasę polecenia lub klasę dziedzinową do testowania warunków danych (definiowanych przez static constraints).

p.s. Nie zapomnieliśmy o konkursie Bloger Roku 2008? Wystarczy wejść na stronę do głosowania na Notatnik, gdzie podajemy nasz adres email i zatwierdzamy. Wiadomość z adresem potwierdzającym pojawia się w naszej skrzynce, klikamy w niego potwierdzając nasz wybór. I tyle. Dziękuję!

14 lutego 2009

Rozdział 4. "Understanding Controllers" z "The Definitive Guide to Grails, Second Edition"

0 komentarzy
Kolejny rozdział 4. "Understanding Controllers" to 40 stron mnóstwa wiadomości z podwórka Grails, a dokładniej o kontrolerach. Mimo wcześniejszej lektury "Beginning Groovy and Grails" (patrz Book review: Beginning Groovy and Grails: From Novice to Professional). Już poprzedni rozdział było trudno zrelacjonować w kilku(nastu) zdaniach, a ten nie będzie łatwiejszy. Ostrzegałem.

Kontroler w Grails jest klasą, która jest odpowiedzialna za obsługę żądań w aplikacji (nie powinno to nikogo dziwić, jeśli przypomnieć sobie, że Grails to realizacja wzorca MVC). Instancje kontrolera tworzone są na nowo dla każdego żądania, więc nie zajmujemy się wcale kwestią odporności kodu kontrolera na wielowątkowość. Klasy kontrolerów znajdują się w grails-app/controllers. Nazwa klasy musi kończyć się Controller. Kontrolery nie mają potrzeby rozszerzać jakiejkolwiek klasy bazowej czy implementować specjalny intefrejs.

Akcje w kontrolerze definiowane są jako pola, do których przypisuje się blok kodu w postaci domknięcia Groovy.

Jeśli nie wskazano akcji do wykonania w URL, Grails wykona domyślną akcję. Wyznaczenie domyślnej akcji to następująca sekwencja: pojedyncza akcja w kontrolerze staje się domyślną, przy większej liczbie jest to index lub dowolna inna wskazana przez właściwość defaultAction, np. (Listing 4-3):
 class SampleController {
def defaultAction = 'list'
def list = {}
def index = {}
}
Właściwość log jest wstrzeliwana do każej klasy kontrolera jako egzemplarz org.apache.commons.logging.Log.

Dowolny wyjątek w Groovy jest wyjątkiem niekontrolowanym (ang. unchecked/runtime exception), stąd nie trzeba zajmować się w ogóle ich przechwytywaniem (chyba, że zajdzie taka potrzeba). Przechwycenie wyjątku w Groovy jest identyczne jak w Javie - blok try-catch.

Lista domyślnych atrybutów dostępnych w kontrolerze (nazwy są samowyjaśniające się): actionName, actionUri, controllerName, controllerUri, flash, log, params, request, response, session i servletContext. Odczyt/zapis parametrów/atrybutów w wielu z nich, odbywa się za pomocą operatorów "dot dereference" czy "subscript", np. request.myAttr, albo session.myAttr = 'pewna-wartość'.

Wyróżniamy 4 zasięgi działania obiektów w Grails - request (pojedyncze żądanie), flash (obecne i przyszłe żądanie), session (sesja) oraz servletContext (współdzielone w całej aplikacji przez całe jej istnienie).

Wyświetlenie komunikatu w odpowiedzi na żądanie z poziomu kontrolera to render 'tutaj tekst do wyświetlenia' z opcjonalnym parametrem contentType, np. (Listing 4-11):
 render 'Witaj użytkowniku'
render text: '<b>Witaj użytkowniku</b>', contentType: 'text/xml'
Warto zauważyć, że wykonanie metody z parametrami w Groovy nie wymaga umieszczenia ich w nawiasach.

Wskazanie danego widoku do wyświetlenia w render (standardowo będzie to strona gsp, której nazwa odpowiada nazwie metody w katalogu odpowiadającym nazwie kontrolera), to zadanie dla parametru view. Możliwe jest wskazanie dedykowanego modelu.
 render(view: "display", model: [ song:Song.get(params.id) ])
Można również wskazać adres widok względnego do grails-app/views, np.
 render(view: "/common/song", model: [ song:Song.get(params.id) ])
Przekierowanie to metoda redirect, która akceptuje mapę jako argument wejściowy, w której umieszczamy niezbędne parametry - action (akcja do wykonania), controller (kontroler), id (parametr id w przekierowaniu), params (mapa obowiązujących parametrów), uri (względny adres przekierowania), url (bezwzględny adres przekierowania).

Kiedy akcja kontrolera zwraca mapę, staje się ona bieżącym modelem dla widoku, np. (Listing 4-14):
 def show = {
[ song: Song.get(params.id) ]
}
Warto zaznaczyć, że w Groovy ostatnie wyrażenie to wartość zwracana (czyli return jest opcjonalny). W tym przypadku widok będzie mógł odczytać song, który wskazany jest w tym modelu.

Można tak (Listing 4-17):
 def save = {
def album = new Album()
album.genre = params.genre
album.title = params.title
...
}
jednakże dla wielu parametrów może to być bardzo pracochłonne, więc łatwiej przypisać parametry z żądania korzystając z automatycznie generowanego w Grails konstruktora:
 def save = {
def album = new Album(params)
...
}
W tym momencie pojawia się uwaga dotycząca potencjalnych ataków, przy wykorzystaniu tego automatycznego ustawiania właściwości z parametrów żądania, które występuje również w Ruby on Rails, Spring MVC, WebWork i in. Rozwiązaniem jest metoda bindData (o której za moment) oraz dokładna kontrola poprawności.

Można również w prosty sposób przypisać wszystkie wartości atrybutów już istniejącego egzemplarza klasy dziedzinowej z wykorzystaniem automatycznie dodawanego atrybutu properties, np.
 def update = {
def album = Album.get(params.id)
album.properties = params
album.save()
}
Przypisane zostaną jedynie parametry, które należą do pustej (domyślnej) przestrzeni nazw, tj. nazwy parametrów są proste, np. title, name. Poprzedzenie nazwy parametru pewną nazwą (przestrzenią klasy dziedzinowej), np.
 <input type="text" name="album.title" />
<input type="text" name="artist.title" />
to możliwość pobrania jedynie pewnego zbioru parametrów, np.
 def album = new Album( params["album"] )
def artist = new Artist( params["artist"] )
Bez względu na przestrzenie nazw parametrów możemy zawężać automatyczne przypisanie wartości do określonych parametrów z bindData, np. (Listing 4-21):
 bindData(album, params, [include:"title"])
include oznacza przypisanie jedynie parametru title, podczas gdy exclude to wyłączenie podanych z przypisania. Możemy również zawęzić rozważane parametry do zadanej przestrzeni nazw parametrów, np.:
 bindData(album, params, [include:"title"], "album")
Kontrola wartości jest oparta na mechaniźmie springowego org.springframework.validation. Każdorazowa kontrola poprawności instancji klasy dziedzinowej to stworzenie obiektu org.springframework.validation.Errors, który jest następnie do niej przypisywany. Wypisanie wszystkich błędów to:
 album.errors.allErrors.each { println it.code }
Możemy jedynie sprawdzić, czy klasa ma jakiekolwiek błędy z hasErrors(), np.
 if (album.hasErrors()) println "Błędy!"
W GSP będzie to znacznik <g:renderErrors>, np.
 <g:renderErrors bean="${album}" />
Na stronie 82. w podrozdziale "Working with Command Objects" pojawia się materiał o obiektach-polecenie (ang. command object), których opisu nie znalazłem w książce "Beginning Groovy and Grails". Całkowite novum!

Klasa polecenia to klasa, która ma wszystkie cechy klasy dziedzinowej, poza trwałością, tj. możliwe jest automatyczne przypisywanie wartości oraz kontrola ich poprawności. Możemy definiować warunki klasy polecenia i sprawdzać ich poprawność, jak w klasie dziedzinowej.

Klasa polecenia znajduje się w katalogu grails-app/controllers, a nawet w samej klasie kontrolera (Groovy wspiera umieszczanie wielu klas w pojedynczym pliku, co może przydać się, jeśli klasa polecenia wykorzystywana jest jedynie przez pojedynczy kontroler). Nazwa klasy polecenia kończy się Command.

Wykorzystanie polecenia w kontrolerze sprowadza się do podania go jako pierwszy argument w akcji, np. (listing 4-24):
 def save = { AlbumCreateCommand cmd ->
...
}
Podczas wykonania akcji, tworzona jest nowa instancja polecenia, do której przypisywane są parametry żądania. Możemy skorzystać z automatycznie generowanej metody validate() do sprawdzenia, czy parametry żądania są spełniają nałożone warunki, np. (Listing 4-26):
 def save = { AlbumCreateCommand cmd ->
if (cmd.validate()) {
...
redirect(action: "show", id:album.id)
} else {
render(view: "create", model: [cmd:cmd])
}
}
Podobnie jak z klasami dziedzinowymi, mamy automatycznie związywany z klasą polecenia obiekt Errors, który w GSP odczytujemy przez
 <g:renderErrors bean="${cmd}" />
Za pomocą atrybutu allowedMethods określamy jakie typy żądania HTTP są dozwolone dla danej akcji (domyślnie wszystkie żadania są dozwolone), np. (Listing 4-28):
 class SomeController {
def allowedMethods = [action1: 'POST', action3: ['POST', 'DELETE']]
def action1 = {}
def action2 = {}
def action3 = {}
}
Niespełnienie warunku to kod błędu "405 Method Not Allowed" w odpowiedzi.

Obsługa przesłania pliku na serwer jest oparta na springowym org.springframework.web.multipart.MultipartHttpServletRequest i znacznikach - <g:uploadForm> (przypisuje formularzowi typ enctype na multipart/form-data) oraz <input> z atrybutem type="file", np. (Listing 4-30):
 <g:uploadForm action="upload">
<input type="file" name="myfile" />
<input type="submit" value="Upload!" />
</g:uploadForm>
Pobranie przesłanego pliku to wykonanie metody MultipartHttpServletRequest.getFile() z parametrem, który odpowiada wartości atrybutu name w input type="file", np. (Listing 4-31):
 def upload = {
// Zmienna file jest typu org.springframework.web.multipart.MultipartFile
// Brak pliku w żądaniu, to getFile() zwraca null
def file = request.getFile('myFile')
// file to przesłany plik
}
Grails, dzięki Spring MVC, automatycznie przypisuje przesłane pliki do atrybutu klasy dziedzinowej zgodnie z regułami - jeśli typ atrybutu to byte[], wtedy przypisane są bajty pliku, a kiedy String zawartość pliku będzie tekstem. Wystarczy nadać nazwę atrybutowi name w input z type="file", która odpowiada nazwie atrybutu klasy dziedzinowej i z pomocą automatycznego przypisywania wartości zlecenia (przez konstruktor i params) Grails zapisze zawartość do atrybutu, np.:
 // klasa dziedzinowa z polem art typu byte[]
class Album {
byte[] art
...
}

// input typu file z atrybutem name o nazwie art
<input type="file" name="art" />

// automatyczne przypisanie wartości przez konstruktor klasy dziedzinowej
def user = new Album(params)
Odczytanie treści zlecenia to request.inputStream.text.

Przesłanie binarnych danych do klienta (w odpowiedzi) to (Listing 4-36):
 def createZip = {
byte[] zip = ... // pobierz zawartość pliku binarnego
response.contentType = "application/octet-stream"
// przeciążony operator << w akcji!
response.outputStream << zip
response.outputStream.flush()
}
Reszta rozdziału przy kolejnej relacji. Zrobiło się trochę przy długo...

p.s. Nie zapomnieliśmy o konkursie Bloger Roku 2008? Wystarczy wejść na stronę do głosowania na Notatnik, gdzie podajemy nasz adres email i zatwierdzamy. Wiadomość z adresem potwierdzającym pojawia się w naszej skrzynce, klikamy w niego potwierdzając nasz wybór. I tyle. Dziękuję!

13 lutego 2009

Relacja z rozdziału 3. w "The Definitive Guide to Grails, Second Edition"

0 komentarzy
Kiedy poznawałem możliwości Grails z perspektywy książki "Beginning Groovy and Grails: From Novice to Professional" sądziłem, że przedstawia ona wszystko, czego mógłbym sobie życzyć. Podchodząc do kolejnej książki o Grails "The Definitive Guide to Grails, Second Edition" jedyne, co gwarantowało sensowność kolejnej lektury, to jej autorzy - liderzy projektu Grails - Graeme Rocher i Jeff Brown oraz wersja, której dotyczyła - Grails 1.1. Miałem pewne obawy, czy dobry programista może być równie dobrym autorem książki informatycznej, ale po kolejnych rozdziałach (już 4 mam za sobą) poznałem tyle nowych cech Grails, które nie znalazły się w pierwszej książce, że mam pewność, że miałem dużo szczęścia rozpoczynając poznawanie Grails w tej kolejności - najpierw "Beginning Groovy and Grails", a teraz "The Definitive Guide to Grails, 2nd Edition". Pewnie wystarczyłaby jedynie ta druga, ale polecam obie i to właśnie w tej kolejności. W ten sposób udaje mi się rozszerzać wiedzę, a nie ją deaktualizować, czy zamazywać. Ta kolejność gwarantuje płynne wejście (przynajmniej teoretyczne) w temat Grails.

Rozdział 3. "Understanding Domain Classes" dotyczy warstwy modelu, który w Grails jest oparty na klasach dziedzinowych (odpowiadają one encjom w JPA). Każda z klas dziedzinowych składa się z właściwości (atrybutów), które domyślnie są mapowane na tabele w bazie danych, aby możliwy był trwały zapis stanu ich egzemplarzy (instancji). Poza kolumnami, które odpowiadają właściwościom klasy dziedzinowej, Grails dodaje kolumny id (unikatowy identyfikator) oraz version (służy do obsługi optymistycznego blokowania).

Warunki jakie muszą spełniać atrybuty klasy dziedzinowej wyrażane są przez statyczną właściwość constraints, której wartością jest domknięcie. Domknięcie może zawierać ograniczenia dla podzbioru atrybutów. Wykonanie save() klasy dziedzinowej to automatyczne wykonanie domknięcia constraints, którego niespełnienie skutkuje błędem kontroli i wstrzymaniem zapisu do bazy danych.

Z klasą dziedzinową związany jest automatycznie generowany atrybut errors - egzemplarz klasy org.springframework.validation.Errors. Jedną z metod interfejsu Errors jest getAllErrors(), więc dostęp do wszystkich błędów kontroli poprawności danej klasy dziedzinowej sprowadza się do:
 <egzemplarz-klasy-dziedzinowej>.errors.allErrors.each { 
// wykonaj operację na pojedynczym błędzie reprezentowanym przez it, np.
println it.defaultMessage
}
Grails dostarcza metodę validate() do każdej klasy dziedzinowej, która zwraca wartość logiczną true, przy poprawnie zakończonej kontroli poprawności. Mamy do dyspozycji również metodę clearErrors(), która czyści zgłoszone do tej pory błędy (błędy są kumulowane).

Poza prostymi warunkami (ograniczeniami) określonymi w domknięciu constraints (w książce wymieniono 19 reguł), możemy zdefiniować własne przez domknięcie validator, która zwraca wartość logiczną true przy poprawnie zakończonej kontroli. Domknięcie validator akceptuje dwa argumenty - pierwszy jest wartością atrybutu do kontroli, a drugi egzemplarz klasy dziedzinowej. Możemy również zwrócić wartość typu String, która będzie identyfikatorem komunikatu błędu (domyślnie <klasa-dziedzinowa>.<atrybut>.validator.error).

Wyłączenie pola z mechanizmu utrwalania (zapisu do bazy) - oznaczenie pola jako ulotne (ang. transient) - to umieszczenie jego nazwy w liście w statycznym atrybucie transients, np.:
 class Wyrazenie {
Integer poleUlotne

static transients = ['poleUlotne']
}
To jest pierwsza z konstrukcji, które nie podobają mi się, bo przede wszystkim wymagają wsparcia IDE, aby nie popełnić błędu - literówki - w nazwie pola ulotnego. Aż się prosi o mechanizm adnotacji, albo stworzenie nowego podobnego do constraints, gdzie podaje się nazwy atrybutów jako metody. Skoro tam można, tu również. Muszę zapytać o to na grupie użytkowników Grails. ...po chwili...Już!

Nie wszystkie atrybuty klasy dziedzinowej muszą odpowiadać polu (instancji) klasy. Wystarczy metoda odczytu (ang. getter) albo zapisu (ang. setter), aby Grails potraktował to jako atrybut klasy. Zasada ulotności działa również i w tym przypadku. Wyłączenie z zapisu to transients z nazwą atrybutu.

Grails, za pomocą GORM, mapuje klasy dziedzinowe bez konieczności plików XML - "objaw" zasady konwencji-ponad-konfigurację. Możliwe jest zdefiniowanie własnego mapowania za pomocą dedykowanego DSL zwanego Custom Database Mapping DSL lub ORM DSL. Warto z niego skorzystać przy wdrażaniu aplikacji z istniejącym schematem bazodanowym, który nie przystaje do domyślnego mapowania w Grails. Użycie ORM DSL jest możliwe przez deklarację publicznego atrybutu statycznego mapping jako domknięcia z column, np. (Listing 3-13):
 class Person {
String firstName

static mapping = {
id column: 'person_id'
firstName column: 'person_first_name'
version false
}
}
Dodatkowo w przykładzie wyłączono wersjonowanie - version false, które służy do optymistycznego blokowania.

Domyślna nazwa tabeli odpowiada nazwie klasy dziedzinowej - zmiana przez table 'nazwa-tabeli' w mapping.

Relacje w Grails - zadeklarowanie atrybutu w klasie dziedzinowej typu innej klasy dziedzinowej to relacja jeden-do-jednego, w której obie strony są równoważne (nie ma właściciela relacji). Określenie strony wiodącej - właściciela relacji - to static belongsTo = [car:Car], gdzie klasa zawierająca tą deklarację będzie automatycznie posiadała atrybut car typu Car. Nazwa atrybutu może być dowolna, ale naturalnie nazwać ją zgodnie z jej typem (bo ta nazwa będzie nazwą bytu w dziedzinie/modelu, więc będzie intuicyjna). Jeśli nie chcemy modelować zależności zwrotnej (zależnej), np. w klasie Car z powrotem do klasy wiodącej, wystarczy w klasie wiodącej zadeklarować belongsTo z typem Class zamiast mapą, np. static belongsTo = Car. Oczywiście w obu przypadkach operacje są przekazywane kaskadowo w dół grafu (powiązań) i np. skasowanie klasy wiodącej będzie kasowało klasę zależną.

Relacja jeden-do-wielu modelowana jest atrybutem hasMany, która jest mapą, która z kolei składa się z klucza będącego automatycznie dodawanym do klasy dziedzinowej atrybutem-kolekcją oraz wartością mapy będącej typem obiektów w kolekcji. Domyślną kolekcją jest java.util.Set. Jeśli potrzebujemy bardziej wyrafinowanej kolekcji musimy zadeklarować jej typ explicite, np. (Listing 3-19):
 class Album {
String title

static hasMany = [songs:Song]

SortedSet songs
}
Klasa dziedzinowa w Grails może rozszerzać inną za pomocą typowej konstrukcji Groovy/Java - słowo kluczowe extends. W/g autorów dobry model charakteryzuje się co najwyżej 2-poziomową hierarchią dziedziczenia. Domyślnym mapowaniem dla hierarchii dziedziczenia jest tablica-per-hierarchia. Włączenie mapowania tablica-per-klasa to deklaracja tablePerHierarchy false w static mapping w klasie macierzystej (najbardziej ogólnej). tablica-per-hierarchia nie pozwala na włączanie warunków typu "niepuste", ale jest szybka w działaniu, podczas gdy tablica-per-klasa to powolne zapytanie złączeniowe (JOIN) na kilku tabelach. Strategia mapowania jest deklarowana na poziomie hierarchii/klasy i może być różna dla różnych klas dziedzinowych.

Włączenie zanurzenia klasy jakby była integralną częścią składową innej, tak aby pojedyncza tabela zawierała kolumny obu to deklaracja static embedded = ['nazwa-atrybutu'], gdzie typ nazwa-atrybutu to klasa zanurzana.

Grails wspiera dwa typy testowania - jednostkowe i integracyjne. Pierwsze znajdują się w klasach w katalogu test/unit, a drugie w test/integration. Rozbudowywanie klas dziedzinowych o metody dynamiczne, np. validate(), save() jest wyłącznie w testach integracyjnych. Stąd testy jednostkowe są bardzo szybkie (bez całego bagażu "integracyjnego"). Każdorazowe wykonanie polecenia grails create-* tworzy poza właściwym bytem, również klasę z testem integracyjnym.

12 lutego 2009

41. spotkanie Warszawskiej Grupy Użytkowników Technologii Java (Warszawa JUG)

0 komentarzy
Warszawska Grupa Użytkowników Technologii Java (Warszawa JUG) zaprasza na 41. spotkanie, które odbędzie się 17.02.2009 (wtorek) o godzinie 18:00 w sali 5440 Wydziału MIMUW przy ul. Banacha 2 w Warszawie.

Temat prezentacji: Envers
Prowadzący: Adam Warski

Envers, "dodatek" do Hibernate'a, a od niedawna jego część, ma jedno zadanie: ułatwić zapamiętywanie historii zmian dokonywanych na encjach oraz jej odczytywanie i wykonywanie na niej zapytań. Problem wersjonowania encji pojawia się dosyć często, czy to z powodu wymagania prowadzenia ciągłego "audytu" danych, czy też z powodu konkretnych funkcji naszego systemu, jak to ma miejsce w przypadku różnych wiki.

Prezentacja rozpocznie się wprowadzeniem do problemu wersjonowania encji oraz sposobu, w jakim rozwiązuje go Envers. Następnie, trochę o założeniach projektu, konfiguracji oraz pojęciu "rewizji" w przypadku encji. W drugiej, głównej części przyjrzymy się różnym funkcjom Enversa "na żywo" z wykorzystaniem gotowego "szkieletu" prostej aplikacji konsolowej; jest ona dostępna do ściągnięcia jako http://www.warski.org/envers-wjug.zip.

Envers jest prosty w obsłudze i nie wymaga dużej ilości kodu, więc jeżeli ktoś chciałby poeksperymentować na własnym laptopie podczas prezentacji, zachęca się do pobrania paczki. Wystarczy mieć Javę, Anta i skonfigurowaną (prawie dowolną) bazę danych. Przyda się też jakieś narzędzie do oglądania zawartości bazy danych.

Na koniec Adam przedstawi "wnętrzności" Envers, czyli pokazać prostotę tworzenia biblioteki ala Envers, dzięki możliwościom, jakie daje Hibernate. Przedstawiony zostanie model metadanych Hibernate'a, "listenery" i inne trochę rzadziej używane funkcje.

Adam Warski jest informatykiem i absolwentem MIMUWu. Przez jakiś czas pracował w JBossie, a teraz na codzień zajmuje się "zwykłym" programowaniem aplikacji w Javie/JEE. W wolnych chwilach pisze Enversa i inne, mniej lub bardziej przydatne programy (szczegóły na jego stronie domowej i blogu).

Planowany czas prezentacji to 1,5 godziny, po której planuje się 15-30-minutową dyskusję.

Wstęp wolny!

Zapraszam w imieniu grupy Warszawa JUG!

11 lutego 2009

Pierwsze kroki z Grails - rozdział 2. w "The Definitive Guide to Grails"

0 komentarzy
Pierwsze dwa rozdziały mam za sobą i gdybym mógł wszystko rzucić pewnie obróciłbym całe 500+ stron książki "The Definitive Guide to Grails", wydanie 2. w jeden dzień. Wciąż zdumiewa mnie mój zachwyt Grails. Wszystko wydaje się być takie proste. Oby zachwyt nim nie minął z rozpoczęciem pierwszej aplikacji webowej. Podczas mojego wystąpienia na 4Developers 7. marca w Krakowie przedstawię Grails praktycznie oraz jego okolice - Groovy i Project Zero. Wartoby do tego czasu rozpracować wszelkie niuanse, aby nie zostać zaskoczonym bardzo podstawowym pytaniem, a już nieciekawie byłoby nie móc skonfrontować mojego entuzjazmu z faktycznymi wdrożeniami w Grails. Stąd wszelkie uwagi i pytania proszę zadawać teraz. Podglądam opis wykładu Python i Django: Szybkie i latwe tworzenie aplikacji webowych Marcina Mierzejewskiego, tak aby uczestnicy mogli porównać ich cechy (Django wymienia się jako protoplastę Grails), ale niestety jego i moje wystąpienie są w tym samym czasie! No cóż, my wiemy co dobre i pojawimy się na Grails, czyż nie?!

Wracając do rozdziału 2. "Getting Started with Grails" to wiele już z tego było podczas mojej lektury "Beginning Groovy and Grails". Niektóre z porównań czy uwag są nowością dla mnie, ale ogólnie nic nowego.

W poprzednim rozdziale autorzy stworzyli zrąb aplikacji gTunes z pojedynczym kontrolerem. Teraz przedstawiają mechanizm rusztowania (ang. scaffolding), za pomocą którego można niezwykle szybko (< 1 minuta) stworzyć prototyp aplikacji CRUD dla danego modelu (klas dziedzinowych). To musi się podobać! I się podoba. Wyróżniamy dwa typy rusztowania: dynamiczny i statyczny. Dynamiczne rusztowanie budowane jest "na bieżąco" podczas uruchomienia aplikacji. Tworzone są widoki (strony GSP) oraz kontroler. Korzysta z mechanizmu prześwietlania (ang. reflection) oraz cechy Groovy. Wystarczy zdefiniować klasę dziedzinową (poleceniem grails create-domain-class) i w odpowiadającym jej kontrolerze (stworzonym poleceniem grails create-controller) definiujemy atrybut scaffold, np.
 package pl.jaceklaskowski.nauczyciel

class SlowoController {
def scaffold = Slowo
}
Dla Groovy Slowo == Slowo.class, więc nie ma konieczności podania rozszerzenia .class jawnie.

Podczas dynamicznego rusztowania Grails rozpoznaje typy poszczególnych atrybutów klas dziedzinowych i dostraja widok, np. dla atrybutów typu String tworzy pole tekstowe w GSP, a dla atrybutów typu Date wyświetli pole rozwijalne do wyboru daty.

Utworzenie relacji jeden-do-wielu między klasami dziedzinowymi możliwe jest dzięki statycznemu polu hasMany, którego wartością jest mapa, w której klucz to nazwa atrybutu, a wartość to typ elementu, z którym klasa jest związana, np.
 static hasMany = [slowa:Slowo]
Deklarując atrybut statyczny hasMany w klasie A ustawiamy jedynie relację jednokierunkową. Klasa docelowa nie ma świadomości uczestniczenia w niej. Dodanie pola o typie klasy źródłowej relacji zmienia relację na dwukierunkową.

Domyślna implementacja metody toString() w Grails to nazwa klasy i identyfikator obiektu. Ma to znaczenie przy wyświetlaniu listy obiektów w relacji jeden-do-wielu, dla widoku klasy wiodącej w relacji.

Rusztowanie statyczne opiera się na szablonach, które można dowolnie modyfikować. W Grails możemy stworzyć konieczne kontrolery i widoki GSP dla klas dziedzinowych - wystarczy grails generate-views, grails generate-controller czy grails generate-all. Dzięki stworzonym artefaktom możemy poznać jak właściwie działa Grails.

Warto wtrącić uwagę autorów (str. 17), że Grails is not just a CRUD framework. Ostatnio chyba za bardzo naciskam na ten CRUD w Grails i może nawet (pochopnie) postawiłem znak równości między nimi. Zdaje się, że pospieszyłem się (chociaż nie widzę innych zastosowań na razie).

Domyślną metodą (domknięciem) wykonywanym przez Grails w kontrolerze jest index. Nie podano akcji w URI, wykonana zostanie index.

Konwencją w Grails jest wiązanie strony GSP dla danego kontrolera i jego domknięcia tak, że dla kontrolera XControler i akcji (domknięcia) y zostanie wyświetlona strona grails-app/views/x/y.gsp.

W GSP możemy skorzystać z metod redirect i render, odpowiednio do przekierowania na podanego w action domknięcia (akcji) oraz wyświetlenia podanej strony (parametr view).

Możemy korzystać z atrybutu scaffold ustawionego na daną klasę dziedzionową, dla której istnieje "nasz" kontroler bez konieczności tworzenia stron GSP - zostaną stworzone dynamicznie (uzupełnią braki).

Dalej autorzy przedstawiają konfigurację reagującą na środowisko, w którym Grails pracuje. Pojawia się konfiguracja bazy danych (za pomocą konfiguracji Hibernate w grails-app/conf/DataSource.groovy) na przykładzie MySQL. Warto zauważyć, że można konfigurować dostęp do bazy danych przez podanie wszystkich parametrów połączenia (per środowisko), albo wskazanie wpisu w drzewie JNDI parametrem jndiName (w dataSource dla danego środowiska).

Stworzenie paczki wdrożeniowej to wykonanie grails war. Domyślna konfiguracja Jetty znajduje się w GRAILS_HOME/conf/webdefault.xml, a uruchomienie WARa to grails run-war. Polecenie grails run-app jest zalecane do uruchamiania "rozwojowego".

Pełne polecenie grails war to grails [środowisko (opcjonalne)] war [katalog-zapisu-war (opcjonalnie)].

p.s. Zainteresowani wsparciem moralnym pisarza Notatnika proszeni są o wzięcie udziału w konkursie Bloger Roku 2008. Wystarczy wejść na stronę do głosowania na Notatnik, gdzie podajemy nasz adres email i zatwierdzamy. Wiadomość z adresem potwierdzającym pojawia się w naszej skrzynce, klikamy w niego potwierdzając nasz wybór. I tyle. Dziękuję!

10 lutego 2009

Wieści z rozdziału 1. w "The Definitive Guide to Grails, 2nd Ed."

7 komentarzy
Zgodnie z zapowiedzią zabrałem się za lekturę książki The Definitive Guide to Grails, 2nd Ed. autorstwa Graeme Rochera oraz Jeffa Browna. Obaj panowie wielce zasłużeni dla projektu Grails, więc nie byłem wcale zdziwiony sposobem, w jaki obaj przedstawiają go - w samych superlatywach i to w niezwykle kwiecistym języku angielskim. Można połączyć przyjemne - poznawanie Grails - z pożytecznym - nauka zwrotów angielskich (nawet pojawiło się zdanie z Future perfect!). Czyta się niezwykle przyjemnie i jakkolwiek wszystko to już czytałem w poprzedniej książce o Grails - Wieści z rozdziału 4. "Introduction to Grails" z "Beginning Groovy and Grails" oraz Tworzenie interfejsu użytkownika w Grails - rozdział 5 z "Beginning Groovy and Grails", to wtedy były dwa rozdziały, a teraz jeden. To samo, a jakby inaczej.

Rozdział 1. "The Essence of Grails" rozpoczyna się mottem Leonardo da Vinci "Simplicity is the ultimate sophistication.", które w zasadzie podsumowuje peany autorów na temat prostoty jaką wprowadziły Groovy i Grails w życie programistów javowych. Należy przypomnieć, że Ci, którzy nie mają przyjemności pracować z tandemem Groovy/Grails jedynie "conjure up unknown horrors and nightmarish thoughts of configuration, configuration, configuration." (z Grails, the Platform, str. 3). Tak przy okazji, w życiu nie spotkałem tego zwrotu conjure up.

Autorzy rozpoczynają książkę przedstawieniem genezy Grails - "dramatically simplify enterprise Java web development." (str. 1). Z konwencją-nad-konfiguracją (ang. CoC - Convention over Configuration), DRY (ang. Don't Repeat Yourself), domyślnymi ustawieniami, gotowym do użycia środowiskiem na bazie Spring Framework, Hibernate, SiteMesh, Jetty, HSQLDB oraz Groovy (przez co mamy dostęp do pełnego zestawu możliwości platformy Java) możemy czerpać garściami z podobnych rozwiązań jak Ruby on Rails, Django czy TurboGears (wspomina się później również CakePHP), które do tej pory były poza zasięgiem programistów javowych. Z Grails mamy tą kwestię rozwiązaną. Jako programiści aplikacji grailsowych nie musimy wiedzieć, że nasze rozwiązania bazują na Spring Framework oraz Hibernate (a już na pewno nie musimy schodzić na poziom ich konfiguracji w XMLu), ale gdy poczujemy taką potrzebę możemy je dodatkowo "dostroić" do naszych potrzeb. Za autorami - Grails jest "a platform with ambitious aims to handle everything from the view layer down to your persistence concerns." (str. 3) oraz jako programista "you have the assurance that you are standing on the shoulders of giants with the technologies that underpin Grails: Spring, Hibernate, and, of course, the Java platform." (str. 1). Mało? Mnie wystarczy. Chyba nie da się nie zauważyć, że trudno byłoby znaleźć mi w tej chwili lepsze rozwiązanie do tworzenia aplikacji webowych niż Grails.

Po tych wyniesieniach przechodzimy do zapoznania się z krokami do utworzenia pierwszej niezwykle trywialnej, bo składającej się z pojedynczego kontrolera, aplikacji grailsowej - gTunes. Aplikacja gTunes to sklep muzyczny ala Apple, Amazon czy Napster. Oczywiście krótki przerywnik w postaci instrukcji instalacji Grails - pobieramy paczkę dystrybucyjną z http://grails.org, rozpakowujemy do wybranego katalogu, ustawiamy zmienną GRAILS_HOME i PATH, i tyle. Przepis na aplikacyjkę to grails create-app, grails create-controller, grails test-app, aby zakończyć grails run-app. Nie wiemy, po co nam każde z tych poleceń?! Wystarczy grails help <polecenie>. Warto podkreślić, że książka opisuje rozwojową wersję Grails 1.1 (aktualnie mamy dopiero Grails 1.1 beta 3). Nie od parady nosi tytuł "The Definitive Guide to Grails".

Jakkolwiek możemy korzystać z pomocy poleceń grails (który jest skryptem Gant) to mamy również możliwość tworzenia wszystkiego "z ręki". Wybór należy do nas samych.

Podkreśla się znaczenie testów jednostkowych i integracyjnych, chociażby ze względu na dynamiczną naturę Groovy, który, podobnie jak inne języki skryptowe - Ruby i Python - nie posiadają takiego arsenału weryfikacyjnego jak statycznie typowane języki, np. Java. Właśnie z tego powodu wielu wybiera Groovy, ale ma to swoje ograniczenia - większość kontroli poprawności programu przesunięta jest na czas jego uruchomienia. Jeśli kiedykolwiek myślałeś o znaczeniu testów w projekcie, teraz masz powód. Szczęśliwie Grails tworzy odpowiedni byt (kontroler, klasę domenową) wraz z odpowiadającymi testami jednostkowym i integracyjnym.

Podczas grails create-controller tworzony jest kontroler z domyślną akcją (domknięciem) index. Zgodnie z konwencją Grails wykonanie index to wyświetlenie strony grails-app/views/<nazwa-kontrolera (bez Controller)>/index.gsp. Poznajemy metodę render, która m.in. wyświetla tekst na stronie.

Wykonanie testów jednostkowych w Grails wiąże się z taką jego konfiguracją opartą na bytach-zaślepkach (ang. mocks and stubs), które symulują faktyczne zachowanie bytu, dla którego stanowią zaślepkę. Grails umożliwia sprawdzenie, czy wykonanie żądania zwróci zadany tekst (zadaną stronę) przez (Listing 1-9):
 controller.index()
assertEquals "tekst z render", controller.response.contentAsString
Grails podstawia za servletowy HttpServletResponse springowy MockHttpServletResponse z metodą contentAsString. Wykonanie grails test-app to uruchomienie wszystkich testów jednostkowych, ale możliwe jest również zawężenie testów do pojedynczego podając parametr do grails test-app <nazwa-klasy-testowej (bez Tests)>. W katalogu test/reports znajdują się raporty z wykonania testów w formacie XML, HTML i TXT.

Uruchomienie aplikacji to wykonanie polecenia grails run-app (domyślnie na porcie 8080). Jeśli podamy parametr -Dserver.port=<numer-portu>, obowiązującym portem będzie podany.

W ten sposób poznaliśmy podstawowe tajniki tworzenia aplikacji. W kolejnym rozdziale autorzy przygotowali dla nas zestaw cudów do sprawnego tworzenia aplikacji typu CRUD z "some of Grails' Create, Read, Update, Delete (CRUD) generation facilities that allow you to flesh out prototype applications in no time" (str. 15). Jeju, jak tak dalej pójdzie to połowa działu rozwoju pójdzie z torbami (!) W tych czasach należy jeszcze bardziej uważać, co się proponuje w projekcie, bo z 30-osobowego zespołu może zostać trzech i jeszcze dwóch może się nudzić. ;-)

09 lutego 2009

Recenzja "Service Oriented Architecture with Java" i nowa o Grails

0 komentarzy
Kolejny dzień odnotowuję jako książkowy. Właśnie co skończyłem czytać książkę "Service Oriented Architecture with Java", kiedy pojawiła się nowa - "The Definitive Guide to Grails, Second Edition". Pierwsza pozostawia wiele do życzenia i jakkolwiek nie zaliczam spędzonego nad nią czasu jako straconego (głównie za wyjaśnienie różnicy między architekturą Hub and Spoke a ESB) to spodziewałem się więcej. Miało być o SOA dla programistów lub architektów javowych, a nie znalazłem w niej więcej niż za dużo teorii a za mało praktyki. Dosyć obszernie (jak na zawartość książki) o JAX-WS, niewiele o Apache Axis, Spring-WS, XFire (CXF), a nawet JBI i OpenEJB. Było również o SCA i SDO, więc wszystko i nic. Zbyt obszernie. Recenzję opublikowałem na Amazonie - Perhaps, your time won't be lost either oraz moim Wiki - Book review: Service Oriented Architecture with Java. Recenzja w języku angielskim ze względu na wymagania wydawcy. Poprosiłem tym samym o kolejne:
Pora zabrać się poważniej za UML, jeśli marzy mi się Sun Certified Enterprise Architect (SCEA).

Wracając do tej drugiej pozycji o Grails, to niezwykle szybko działają w Apress. Dopiero, co wysłałem recenzję do wydawnictwa z prośbą o kolejne książki (patrz: Groovy i Grails, Bloger Roku 2008, 4Developers i Refaktoryzacja) i mijają niecałe 4 dni, a książka już u mnie! Miła niespodzianka poniedziałkowa. Oczywiście (chwilowo) zarzucam lekturę JAX-WS na rzecz "The Definitive Guide to Grails, Second Edition". W końcu taki był plan. Książka to "cegła" na 570 stron autorstwa samego Graeme Rochera, który jest głównodowodzącym w projekcie Grails, więc pewnie odpłynę do końca tygodnia. Właśnie zbudowałem nowiuteńką wersję rozwojową Apache Geronimo 2.2 i z nim zamierzam popróbować uruchomienie aplikacji grailsowych. Na początek zabiorę się za artykuł z developerWorks - Apache Geronimo on Grails.

p.s. Konkurs Bloger 2008 roku trwa, w którym zgłosiłem Notatnik. Wystarczy wejść na stronę do głosowania na Notatnik, gdzie podajemy nasz adres email i zatwierdzamy. Coś tam pojawia się w naszej skrzynce, klikamy link potwierdzający w wiadomości i voila. Dziękuję!