22 stycznia 2009

Wieści z rozdziału 4. "Introduction to Grails" z "Beginning Groovy and Grails"

Po materiale o Groovy przyszła w końcu pora na coś grailsowego - rozdział 4. "Introduction to Grails" w książce Beginning Groovy and Grails: From Novice to Professional. Niech nie przeraża nikogo 40 stron w tym rozdziale, bo wiele w nim zrzutów ekranu. W zasadzie można przyjąć, że przeczytanie tego rozdziału i 2 kolejnych daje pogląd na możliwości Grails. Reszta książki dotyka już specjalizowanych tematów, które każdy szkielet webowy w jakimś tam stopniu udostępnia, więc i w Grails nie mogło tego zabraknąć. O tym jednak w kolejnych relacjach.

Rozdział zaczyna się od przedstawienia jak to programista aplikacji webowych ma źle. Pierwsza sekcja zaczyna się od "Let's face it: developing web application is hard". Po tym i kilku następnych zdaniach miałem wrażenie, że Ci, którzy zajmują się tworzeniem serwisów internetowych korzystając z technologii javowych to jacyś straceńcy - mnogość technologii przytłacza, a będzie ich jeszcze więcej. Normalnie mamy przesr...chlapane! Jak można się domyśleć, autorzy, czarną wizję świata tworzenia aplikacji webowych rozpościerają przed nami, aby przedstawić panaceum - i to ma być właśnie Grails. I w dodatku aplikacje na platformie Java EE nie są wystarczająco zwinne (ang. agile), a na pewno za dużo w nich XMLa. W Grails za to jest inaczej. Jest to szkielet webowy, który naszpikowany jest najlepszymi praktykami, jak konwencje-zamiast-konfiguracji, wbudowane testy jednostkowe oraz wielką trójcę świata projektów otwartych - Spring Framework, Hibernate oraz SiteMesh (może zbyt dużego określenia użyłem dla SiteMesh, ale niech mu tam będzie). Dodając do tego podwaliny w postaci języka Groovy i platformy Korporacyjnej Javy (Java EE), a nawet wybredni powinni znaleźć coś w Grails dla siebie. Nic tylko programistyczna nirvana (przynajmniej z perspektywy autorów)!

Grails jest szkieletem opartym o wzorzec Model-View-Controller (MVC). Model w Grails to klasy dziedzinowe (ang. domain classes - w Java EE nazywa się je po prostu encjami), które są automatycznie bytami trwałymi. Dzięki Hibernate mogą również tworzyć cały schemat bazodanowy podczas uruchomienia. Opierając się na konwencji nazewniczej korzystamy z IoC (automatycznego przekazywania zależności). Kontroler (zarządca) powoływany jest na czas trwania pojedynczego żądania. Domyślny widok oparty jest na Groovy Server Pages (GSP). Dodajemy do tego system szablonów oraz biblioteki znaczników i powinniśmy mieć komplet narzędzi do tworzenia aplikacji webowych.

Z Grails dystrybuowane są: kontener servletów Jetty, baza dnaych HSQLDB (domyślnie w trybie "w pamięci" z każdorazowym tworzeniu schemtu bazodanowego podczas uruchomienia aplikacji) oraz system budowania Gant i wykonywania testów (bazujący na JUnit). Na pewno znacznie skraca czas zestawiania środowiska programistycznego, co kwalifikuję jako plus.

Grails dystrybuowany jest na licencji Apache Software License (ASL) 2.0 pod adresem http://grails.org.

Dla mnie najbardziej interesującą sekcją wprowadzenia do Grails było wyjaśnienie pojęcia scaffolding. Spotykałem się z tym terminem wielokrotnie i wciąż myślałem, że dotyczy to pewnej czynności (zmyliła mnie owa końcówka -ing, podobnie jak slippery, które bardziej przypomina mi rzeczownik niż przymiotnik). Scaffolding to po prostu rusztowanie, które budowlańcom pozwala na konstrukcję domu, a nam, programistom, a w zasadzie to samemu Grailsowi, na dynamiczne budowanie zrębów aplikacji typu CRUD (ang. Create-Read-Update-Delete) na bazie wybranego przez nas modelu bez specjalnego wysiłku programistycznego.

Grails opiera utrwalanie danych relacyjnych na bazodanowe (ang. ORM - Object-Relational Mapping) na Grails Object Relational Mapping (GORM). To GORM modyfikuje zachowanie naszego modelu, aby automatycznie udostępnić funkcjonalność CRUD, więc jedyne co my musimy zrobić, to po prostu utworzyć klasę bez konieczności dbania o dodatki ORM. Zamiast POJO (ang. Plain Old Java Object - stary dobry obiekt javowy) w świecie Groovy mówi się o POGO (ang. Plain Old Groovy Object - stary dobry obiekt groovy).

Grails sam w sobie jest jedynie szkieletem, który udostępnia bardzo podstawowe funkcjonalności do tworzenia aplikacji webowych. Resztę "załatwiają" wtyczki, o które można rozszerzyć nasz projekt grailsowy.

Do dyspozycji mamy trzy popularne biblioteki ajaxowe: script.aculo.us, Rico oraz Prototype. Fundamety Web 2.0 są dostępne od ręki, co również zaliczam na plus.

Praca z Grails, poza programowaniem, to wydawanie poleceń grails, które oparte są na Gant - system budowania napisany w Groovy do tworzenia bezxmlowych skryptów Apache Ant.

Dalsza zawartość rozdziału to przedstawienie instalacji Grails (pobierasz, rozpakowujesz, zmieniasz PATH i voila), aplikacji Collab-Todo, która będzie tworzona w książce jako aplikacja wzorcowa do prezentacji cech Grails i przegląd podstawowych poleceń grails. Polecenia rozpoczyna mechanizm scaffolding. Od razu jednak autorzy nie pozostawiają złudzeń - aplikacja wykorzystująca scaffolding jest przydatna wyłącznie na etapie prototypowania. Mechanizm ten opiera się na prześwietlaniu obiektów (ang. reflection) podczas działania aplikacji, a to destrukcyjnie wpływa na jej szybkość.

Budowanie aplikacji rozpoczynamy od stworzenia struktury katalogowej wraz z podstawowymi plikami poleceniem grails create-app <nazwa-aplikacji>. Następnie wczytujemy projekt do naszego wybranego IDE. I tutaj niestety muszę nadmienić, że o NetBeans IDE ani słowa w książce. Wspomina się jedynie o Eclipse IDE, TextMate oraz IntelliJ IDEA. Polecenie create-app tworzy pliki projektowe dla Eclipse i TextMate.

Warto zapoznać się z czterema plikami konfiguracyjnymi w katalogu grails-app/config. Poza UrlMappings.groovy, wszystkie są świadome środowiska, w którym działają - development, test oraz production. Można definiować własne, ale o tym dopiero w rozdziale 12. Mamy więc BootStrap.groovy, w którym definiujemy obsługę startu i zatrzymania aplikacji, Config.groovy, w którym umieszczamy całą konfigurację, DataSource.groovy dla konfiguracji JDBC i JNDI oraz wspomniany UrlMappings.groovy z przez nas definiowanymi URLami.

Uruchomienie aplikacji Grails to grails run-app w katalogu projektu grailsowego. Wystarczy następnie uruchomić przeglądarkę i wskazać na http://localhost:8080/<aplikacja>, aby cieszyć oko swoim dziełem grailsowym. Nie spodziewajmy się wiele, skoro nic poza strukturą katalogową nie stworzyliśmy - potrzeba klas dziedzinowych (tych, których stan będzie utrwalany w bazie danych i dla których będzie stworzony dedykowany kontroler i widoki - mówimy w końcu o aplikacji CRUD). Potrzebujemy uruchomić polecenie grails create-domain-class <nazwa-klasy>. Dzięki GORM do niej dołączone zostaną metody do obsługi bazy danych, m.in. delete(), save() czy count(). Kasowanie wszystkich rekordów w bazie danych sprowadza się do wykonania <nazwa-klasy-dziedzinowej>.list()*.delete() (pamiętamy o istnieniu operatora spread w Groovy - <lista>*.metoda(), czyli uruchom metoda() na każdym obiekcie w lista?). Autorzy zwracają jednak uwagę na działanie metody list(), która wczytuje *wszystkie* rekordy z bazy - bardzo pamięciożerna operacja, która może zakończyć się OOME (=OutOfMemoryError).

Sprawdzenie poprawnego działania aplikacji można wykonać poleceniem grails test-app, która poza wyświetleniem wyniku testów na konsoli zapisze je w HTML w katalogu test/reports/html.

GORM dostarcza dynamicznie do naszych klas dziedzinowych dwóch pól id oraz version, za pomocą których Hibernate obsługuje zarządzanie trwałymi danymi w bazie danych.

W naszej klasie dziedzinowej można umieścić również pewne ograniczenia, jakie musi spełniać obiekt konstrukcją static constraints. W niej definiujemy, np. czy pole jest wymagane (wystarczy napisać nazwę metody dla danego pola, np. <pole>()), długość pola przez maxSize, lub określić, że pole może być puste (jako argument do metody pola <pole>(nullable: true)).

Kolejnym etapem podczas tworzenia aplikacji jest grails create-controller <nazwa-klasy-dziedzinowej>, który utworzy kontroler w katalogu controllers oraz test w test/integration. Powstanie również katalog grails-app/views/<nazwa-kontrolera>. Domyślnie, utworzony kontroler zawiera coś podobnego do:
 class TodoController {
def index = {}
}
Zamieniając def index = {} na def scaffold = <klasa-dziedzinowa> "uzbrajamy" naszą aplikację w mechanizm scaffolding. Utworzenie kontrolera rozbudowuje stronę startową aplikacji z jego wskazaniem. I tak dla każdego kontrolera. Ich liczba jest nieograniczona.

Autorzy nadmieniają o niedoskonałym momentami przeładowaniu aplikacji przez kontener Jetty. Czasami potrzebny jest jego restart, aby zmiany w modelu i kontrolerze aplikacji zostały rozpoznane. Wystarczy zatrzymać serwer kombinacją Ctrl-C, albo poleceniem kill i uruchomić go ponownie przez grails run-app.

Końcówka rozdziału dotyczy mapowania klas dziedzinowych w relacji jeden-do-wielu za pomocą atrybutów belongsTo oraz hasMany. Wystarczy do klasy dziedzinowej dodać
 User user

static belongsTo = [User]
aby klasa, która to zawiera była w relacji z klasą User przez pole user. Skasowanie obiektu user powoduje skasowanie zawierającej go klasy dziedzinowej związanej przez belongsTo.

Na koniec wyjaśnienie hasMany, tj. dodanie
 static hasMany = [todos: Todo, categories: Category]
powoduje utworzenie relacji jeden-do-wielu z klasą Todo. Kolekcje dostępne są przez atrybuty todos oraz categories. I tym samym kończy się rozdział 4 "Introduction to Grails". Następne 2 zapowiadają się jako najcięższe i jednocześnie najbardziej wartościowe.

Jest wiele materiału na Sieci o Grails, ale ja proponuję przyjrzeć się jednemu, który wpadł mi w ręce dzisiaj w pRaSSówce - Mastering Grails: Give your Grails applications a facelift. Wydaje się być wspaniałym podsumowaniem mojej dzisiejszej relacji. Jeśli zajrzy się do całej serii Mastering Grails na developerWorks będziemy mieli nie tylko okazję przeczytać ten artykuł, ale w dodatku 12 kolejnych (!) Jeśli tylko ma się czas, to można stać się ekspertem Grails w jeden dzień! ;-)

p.s. Konkurs Blog Roku 2008 jeszcze trwa i wszedł w 3. etap, ale już bez Notatnika - za mało głosów i wypadł z gry. Wszystkim głosującym bardzo dziękuję za udział w zabawie. Wierzę, że nie była zbyt kosztowna ;-)

5 komentarzy:

  1. Może dopowiem:
    GORM oprócz pól id i version, obsługuje automatycznie pola typu Date: dateCreated i lastUpdated. Wystarczy je zadeklarować w klasie, aby były automatycznie wypełniane przy tworzeniu/aktualizacji. Jeśli chodzi o scaffolding to
    def scaffold = KlasaDziedzinowa
    można zastąpić przez
    def scaffold = true

    W takim przypadku nazwa klasy dziedziny zostanie zgadnięta na podstawie nazwy kontrolera (czyli klasa Todo bo kontroler nazywa się TodoController).

    Pozdrawiam

    OdpowiedzUsuń
  2. Dzięki Łukasz! To jeszcze bardziej otwiera oczy, czego możnaby wymagać od innych szkieletów webowych. Bajka! Mam już jednak jedną z rzeczy w Grails, która mi się nie podoba. Na razie jej nie wyjawię, bo może być wynikiem mojej słabej jego znajomości (podpowiedź: GSP i edytory HTML).

    p.s. Coraz bardziej widzę potrzebę stworzenia platformy, gdzie tego typu informację byłyby umieszczane przez samych zainteresowanych, a nie wymagałyby intensywnego prowadzenia własnego bloga.

    OdpowiedzUsuń
  3. Czy chodzi Ci o tagi gsp umieszczane w tagach html?

    Jeśli tak, to istnieje możliwość skorzystania z innej konstrukcji,np:
    zamiast
    <g:message code="home" default="Home"/>
    można napisać
    ${message(code:"home",default:"Home")}.
    Można tak zastąpić każdy tag. Jeśli tag pochodzi z innej przestrzeni nazw to
    wystarczy napisać przed tagiem nazwę tej przestrzeni i kropę:np.:
    ${g.message(code:"home",default:"Home")}

    Oczywiście dla przestrzeni "g" nie jest to wymagane.

    Jeszcze przychodzi mi do głowy problem z domknięciami. Jeśli definiujemy je w pliku .gsp, to Netbeans głupieje i źle oznacza pasujące klamerki i błędnie koloruje.

    Jeśli to inny problem to mam nadzieję, że mimo wszystko podzielisz się z nami ;)

    Pozdrawiam.

    P.S. Jeśli chodzi o możliwość wykazania się "zainteresowanym", to Grzesiek Duda rozkręca gazetkę Java Express. Ty Jacku znasz ją, bo z tego co widzę będziesz niedługo na jej łamach opowiadał o Spring DM ;-). Ale może inni czytelnicy Twojego bloga będą mieli chęć spróbować sił jako autorzy artykułów. Dla zainteresowanych: http://dworld.pl/java-express/

    OdpowiedzUsuń
  4. Cześć,
    Warto też zanotować istnienie polecenia "grails generate-all [klasa_dziedzinowa]", które jest dobrą alternatywą dla rusztowania. Utworzy ono kontroler z już zaimplementowanymi metodami pozwalającymi na cały CRUD.
    Pozdrawiam

    OdpowiedzUsuń
  5. @Łukasz: Właśnie to mnie najbardziej zabolało w Grails - nie cierpię rozwiązań, które psują edytory HTML wprowadzając nieznane konstrukcje semi-programistyczne. Czym ich mniej tym lepiej - Apache Wicket na razie wygrywa na tym polu z wicket:id. Może jednak jeszcze poznam, dlaczego Grails z GSP nie są stracone ;-) Piszę o tym trochę w relacji z rozdziału 5. "Beginning Groovy and Grails".

    OdpowiedzUsuń