30 marca 2009

Rozdział 8. o wsparciu dla Ajax w Grails z DGG2

0 komentarzy
Rozdział 8. w książce "The Definitive Guide to Grails, Second Edition" przedstawia wsparcie dla technologii Ajax w Grails.

Działanie technologii Ajax opiera się na wykorzystaniu klasy XMLHttpRequest jako kontrolki ActiveX lub bezpośrednio wspierany obiekt JavaScript. Ukrycie różnego wsparcia przez przeglądarki to zadanie dla szkieletów ajaksowych, np. Prototype czy Dojo Toolkit. Podobno pracuje się nad standardem, który ujednoliciłby pracę z różnymi szkieletami ajaksowymi (podobnie do JPA czy JDBC), ale póki co prace trwają. W Grails domyślnie dostarczanymi szkieletami ajaksowymi są Prototype oraz Scriptaculous, a za pomocą mechanizmu wtyczek można dodać inne, np. Yahoo UI (yui lub yui3), Dojo (dojo), Ext-JS (ext) i jQuery (jquery).

Grails ma dedykowane znaczniki ajaksowe w GSP do tworzenia odnośników, formularzy i pól tekstowych, np. g:remoteLink (wykonanie akcji synchronicznie).

Pracę z biblioteką ajaksową rozpoczynamy od jej wskazania przez <g:javascript library="prototype" /> w sekcji <head>. W wyniku zależności biblioteki ajaksowej zostaną dołączone do strony.

Konstrukcja (Listing 8-4):
 <g:remoteLink controller="timer" action="showTime" update="time">
Aktualny czas
</g:remoteLink>
<div id="time"></div>
spowoduje, że każdorazowe wciśnięcie łącza "Aktualny czas" spowoduje wykonanie akcji showTime (aktualnego) kontrolera (atrybut action), którego wynik zostanie wyświetlony w elemencie DOM o identyfikatorze time (atrybut update) - w tym przypadku będzie to obszar div.

Zmiana biblioteki to instalacja jej wtyczki grailsowej (polecenie grails install-plugin) oraz jej wskazanie przez g:javascript. Nie trzeba nawet zatrzymywać aplikacji!

Znacznik GSP - g:formRemote - to udoskonalona wersja g:form do zatwierdzenia formularza jako żądanie ajaksowe. Przy migracji g:form na f:formRemote należy pamiętać, aby odpowiedź z kontrolera nie była pełną odpowiedzią (pełną stroną), a jedynie jej wycinkiem (szablonem w nomenklaturze Grails), który wpasujemy w resztę bieżącej strony. Główną różnicą między stronami a szablonami to nazwa pliku rozpoczynająca się od podkreślnika.

Dla przypomnienia: jeśli podzielimy części strony na szablony to złożenie ich w całość w postaci pojedynczej strony jest możliwe z wykorzystaniem znacznika g:render z atrybutem template, np. (Listing 8-6):
 <div id="loginBox">
<g:render template="/user/loginForm" />
</div>
Tak przygotowany szablon możemy wykorzystać jako odpowiedź w zapytaniu ajaksowym.

Odpowiedź na zatwierdzenie formularza z wykorzystaniem Ajax trafia do elementu DOM (strony) wskazanego przez atrybut update znacznika g:formRemote, np. (Listing 8-7):
 <g:formRemote name="loginForm" url="[controller:'user','action:'login']" 
update="loginBox">
...
</g:formRemote>
Wyświetlenie wycinka strony (szablonu) to wskazanie go w akcji kontrolera przez metodę render z atrybutem template (zamiast dotychczasowego view), np. (Listing 8-9):
 def login = {
...
render(template:'welcomeMessage')
}
Każdy ze znaczników ajaksowych w Grails wspiera dwa atrybuty: before i after, za pomocą których możliwe jest umieszczenie dowolnego kodu JavaScript, który będzie wykonany, odpowiednio, przed i po żądaniu ajaksowym. Kod JavaScript w atrybucie after zostanie wykonany bez względu na to, czy żądanie zakończyło się pomyślnie, czy nie, a zaraz po wysłaniu żądania (innymi słowy: nie mylić z onComplete!).

Do obsługi zdarzeń ajaksowych wykorzystujemy atrybuty onSuccess, onFailure, onLoaded, onComplete oraz on<KOD_BŁĘDU> znaczników ajaksowych w GSP, np. (Listing 8-12):
 <g:formRemote ... onLoading="showProgress();" 
onComplete="hideProgress();">
...
</g:formRemote>
Podczas prezentacji obsugi Ajax w Grails autorzy wykorzystują Amazon Associates Web Services do zdalnego pobrania prezentowanych w aplikacji okładek płyt. Ponownie prezentowane są te cechy klas usługowych w Grails, które powodują, że są one idealne do realizacji tego zadania. Nie obsługują interakcji przeglądarka-aplikacja, a jedynie zawierają pewną funkcjonalność (logikę) biznesową systemu, którą można współdzielić w wielu innych miejscach przez wykorzystanie mechanizmu wstrzeliwania zależności (ang. dependency injection). Domyślnie klasy usługowe są transakcyjne, tj. wszystkie publiczne metody są opakowane w transakcję zarządzaną przez Spring Framework, co sprawia, że operacje bazodanowe są właściwie izolowane i gwarantuje się ich atomowość. Wyłączenie transakcji w klasie usługowej to ustawienie statycznego atrybutu transactional na false, tj.
 static transactional = false
Ciekawostką, o której nie pamiętałem w związku z klasami usługowymi, jest możliwość konfiguracji ich atrybutów za pomocą konfiguracji w pliku grails-app/conf/Config.groovy. Technika nosi nazwę konfiguracji nadpisywania atrybutów (ang. property override configuration), a jest grailsową realizacją funkcjonalności znanej z Spring Framework. Każda klasa usługowa w Grails jest ziarnem springowym typu Singleton. Nazwa ziarna wyliczana jest na podstawie nazwy klasy. Blok beans w Config.groovy służy do ich konfiguracji, np. (Listing 8-18):
 beans {
albumArtService {
accessKeyId = "ciąg znaków będący kluczem dostępowym do serwisu Amazon"
}
}
Możliwe jest określenie wartości poszczególnych klas usługowych per środowisko, jak to ma miejsce w innych plikach konfiguracyjnych Grails, które są niczym innym jak skryptami Groovy.

Przy prezentacji rozwiązania, pojawia się również wzmianka o metaprogramowaniu w Grails, a w zasadzie Groovy, gdzie poznajemy sposób przełonięcia wykonania metody udostępnianej przez klasę dostarczaną w bibliotece Amazonu. Wystarczy skorzystać z atrybutu metaClass i przypisać domknięcie, aby dotychczasowa implementacja została zastąpiona naszą. Bardzo użyteczny mechanizm podczas testowania, kiedy klasa uczestnicząca w testach kontaktuje się ze światem zewnętrznym, a chcielibyśmy tego uniknąć.

Istnieje jeszcze jeden plik konfiguracyjny, w którym definiujemy ziarna springowe do użycia w naszej aplikacji grailsowej - grails-app/conf/spring/resources.groovy. Wszystkie zdefiniowane ziarna springowe w tym pliku mogą być wstrzeliwane do klas obsługiwanych przez mechanizm wstrzeliwania zależności w Grails - kontrolery, klasy usługowe i klasy znaczników GSP. Wystarczy, więc następująca konstrukcja, aby stworzyć ziarno albumArtCache, które reprezentuje definicję ziarna EhCache (Listing 8-21):
 beans = {
albumArtCache(org.springframework.cache.ehcache.EhCacheFactoryBean) {
timeToLive = 300
}
}
Funkcjonalność uzupełniania wprowadzanego tekstu lub automatycznego wyszukiwania można zrealizować w Grails przy pomocy <g:remoteField>, który przesyła swoją wartość przy każdorazowej zmianie. Atrybut paramName określa nazwę parametru żądania, jaki będzie wysłany do akcji kontrolera (atrybut url z parametrami controller i action), np. (Listing 8-30):
 <g:remoteField name="searchBox" update="musicPanel" paramName="q" 
url="[controller:'store',action:'search']" />
Domyślna nazwa parametru, w którym jest wprowadzana wartość, to value.

Wspomina się jeszcze o funkcjonalności wyszukiwania z pomocą wtyczki Searchable opartej na projektach Compass oraz Apache Lucene, aby na zakończenie zaprezentować, jak to nazwali autorzy, "a compelling use of Groovy closures to deal with exceptions" (Listing 8-32):
 def search = {
...
def searchResults = [albumResults: trySearch { Album.search(q, [max:10]) } ]
}

def trySearch(Closure callable) {
try {
return callable.call()
} catch (Exception e) {
log.debug "Search Error: ${e.message}", e
return []
}
}
Niezłe, co?! Kolejny rozdział niemniej interesujący i niezwykle obszerny "Creating Web Flows", który dotyczy integracji Spring Web Flow z Grails. Prawie 50 stron czystej wiedzy.

27 marca 2009

Rozdział 7. o umiędzynarodowianiu i lokalizacji w Grails z DGG2

3 komentarzy
Powrót do relacji z lektury książki "The Definitive Guide to Grails, Second Edition". Dzisiaj o umiędzynarodowianiu i lokalizacji aplikacji w Grails.

Grails udostępnia różne mechanizmy umiędzynarodowienia i lokalizacji aplikacji. Zakłada się, że wszystkie komunikaty aplikacji w Grails umieszczone są w dedykowanych plikach komunikatów w katalogu grails-app/i18n. Domyślne komunikaty w języku angielskim są w grails-app/i18n/messages.properties, a pozostałe tłumaczenia w innych. Zasada rządząca, który plik jest przypisany danemu językowi, odpowiada zasadom w Javie (patrz dokumentacja klasy java.util.Locale). Komunikaty są wykorzystywane m.in. do informowania użytkownika o błędach walidacji danych w klasach domenowych czy poleceń (np. przy wywołaniu validate() lub save()).

Grails udostępnia znacznik GSP g:message, który korzysta z plików komunikatów w grails-app/i18n. Obowiązkowy atrybut code wskazuje na dany identyfikator komunikatu, np. (Listing 7-3):
 <g:message code="gtunes.welcome" />
Jak to bywa w innych szkieletach webowych, tak i w Grails, domyślnym językiem jest język domyślny w przeglądarce. Wystarczy jednak wywołać URL z parametrem lang, aby go zmienić, np. http://serwer/aplikacja/?lang=pl. Możemy również zdefiniować własne mapowanie, w którym explicite wskażemy miejsce w adresie URL, z którego aplikacja (a właściwie sam Grails) będzie pobierała wskazanie na język, np. (Listing 7-7):
 class UrlMappings {
static mappings = {
"/store/$lang"(controller:'store')
}
}
W ten sposób wywołanie http://serwer/aplikacja/store/pl będzie wskazaniem na polską wersję językową aplikacji.

Istnieje możliwość parametryzowania komunikatów konstrukcją {#}, gdzie # oznacza numer parametru, np.
 komunikat.powitalny = Witaj {0}!
Znacznik GSP - g:message - ma opcjonalny atrybut args, który jest listą parametrów wejściowych dla komunikatu (kolejność ma znaczenie), np.
 <g:message code="komunikat.powitalny" args="[Jacek]" />
Oczywiście (jak to w wielu innych miejscach w Grails) przekazane parametry mogą być wyliczane dynamicznie, np.
 <g:message code="komunikat.powitalny" args="[session.user?.firstName ?: 'Nieznajomy']" />
Pamiętamy znaczenie operatora ?: (operator Elvis)? Jest on skrótem dla javowego operatora "warunek? wartość-dla-true : wartość-dla-false". W przypadku braku wartości zwracana jest ta po ?:.

W Grails mamy do dyspozycji ziarno springowe messageSource (typu org.springframework.context.MessageSource), które może być przekazane (wstrzyknięte) do dowolnego artefaktu grailsowego - kontrolera, znacznika GSP czy innych ziaren springowych. Za jego pomocą możemy pobrać komunikaty z poziomu kodu w Javie. Wystarczy zadeklarować referencję (atrybut) w naszej klasie, aby Grails (właściwie Spring) ją poprawnie zainicjował, np. (Listing 7-18):
 class StoreController {

def messageSource

def index = {
def msg = messageSource.getMessage('komunikat.powitalny', ['Jacek'], new Locale('pl'))
}

}
Już w nadchodzącą sobotę - 4.04.2009 - java4people w Szczecinie będę miał okazję przedstawić Grails praktycznie. Zapraszam! Zachęcam również do zadawania pytań/składania propozycji tematów do omówienia podczas mojego wystąpienia. Szkoda, abyście poświęcili swój czas na nic. Graeme zbudował kompletną aplikację ala twitter w 40 minut (Webinar: Twitter with Grails in 40 Minutes). Może i mi uda się coś fajnego skonstruować w tym czasie?! Pomoc wskazana.

26 marca 2009

44. spotkanie Warszawa JUG - "Java dla dużych chłopców, czyli Lego Mindstorms i leJOS" z Jackiem Kunickim

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

Temat prezentacji: Java dla dużych chłopców, czyli Lego Mindstorms i leJOS
Prelegent: Jacek Kunicki

Podczas prezentacji Jacek przedstawi możliwości drzemiące w klockach Lego wzbogaconych o czujniki i silniki, czyli w zestawie Mindstorms. Będzie zarówno część teoretyczna, która pokaże jak można oprogramować Lego Mindstorms, jak również praktyczna demonstracja przykładowego robota - zarówno w działaniu, jak i od kuchni. Aby uczynić zadość tematyce grupy, prelegent omówi programowanie Lego Mindstorms z wykorzystaniem opartego o Javę - leJOS.

Jacek Kunicki jest absolwentem Wydziału EiTI PW. Obecnie pracuje jako programista Java EE w Javatech. Poza kwestiami zawodowo-technologicznymi pasjonują go piesze wycieczki po górach, zaś w tzw. międzyczasie obserwuje zjawiska socjologiczne występujące we współczesnym Internecie - zwłaszcza w serwisach społecznościowych. Jacek prowadzi bloga, w którym dość nieregularnie umieszcza wpisy o przeróżnej tematyce.

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

Wstęp bezpłatny!

Zapraszam w imieniu grupy Warszawa JUG!

20 marca 2009

Services w NetBeans IDE 6.7 i NetCAT 6.7

9 komentarzy
Jakimś cudem trafiłem do zakładki Services w najnowszej wersji rozwojowej NetBeans 6.7. Ach, to było zapewne, po przeczytaniu wpisu Adama Biena o GlassFish v3 i Equinox Interesting announcement: Glassfish V3 Can Run On Equinox, albo może po tym, kiedy zauważyłem, że dawne paczki netbeans-trunk-nightly-<data>.zip są teraz dostępne jako netbeans-trunk-nightly-<data>-ml.zip. A może po wcześniejszych zapowiedziach aktualizacji GlassFisha do wersji v2.1. Tak czy owak, znalazłem się w zakładce Services.

I tutaj zdumienie - nie zastałem tego, czego oczekiwałem, tylko całkiem nowe węzły Hudson oraz Issue Tracking. Brakuje przynajmniej Servers (pewnie zasługa nowej funkcjonalności NetBeans 6.7 - Ergonomic IDE (czytaj o tym w Ergonomic IDE == Fitness Forever == NetBeans Features on Demand ), która włącza tylko te funkcjonalności, które są w danej chwili potrzebne/wymagane).

Zobaczmy, jakie repozytoria są wspierane domyślnie przez Issue Tracking.

Szału nie ma - tylko Bugzilla?! A gdzie JIRA? Myślałem, że może to ma związek z brakującą wtyczką do niej, ale nic nie znalazłem w Tools -> Plugins.

Coś się zmienia, to dobrze, ale jak to bywa, nie wszystkich można ustatysfakcjonować. Pewnie wsparcie dla JIRA będzie niebawem.

Sprawdziłem Hudsona i skonfigurowałem go z http://deadlock.netbeans.org/hudson.

I działa. Nie pracuję na codzień z Hudson, więc nie wiem, czy to wystarczy do pracy z nim, ale właśnie dlatego, że jest, będę miał okazję nauczyć się z tego korzystać - chciał, czy nie chciał. Może nawet za chwilę stwierdzę, że nie wyobrażam sobie życia bez tego?! ;-)

Interesująca jest ta ikona w dolnym prawym rogu - ta z 5-tką. Kiedy otworzyłem NetBeans od razu rzuciła mi się w oczy ta malutka ikona obok (wciąż na szaro, nawet teraz). Wciskając tą 5-tkę dostajemy informacje o wynikach budowania, które wymagają interwencji.

To jest dobre. Nie trzeba zaglądać do poczty, RSSa, czy innego mechanizmu informowania o wpadce w systemie automatycznego budowania, aby się zorientować, że coś jest nie tak i...już idą po nas (i będzie trzeba wybierać tabletkę niebieską, albo czerwoną, i takie tam...).

A tak jeszcze w temacie, rozpoczęły się zapisy na kolejny NetBeans Community Acceptance Tests (NetCAT) 6.7, czyli program akceptacji wydania NetBeans do publicznego użycia - jako finalny produkt. Jirka, głównodowodzący programem, wysłał zaproszenie na grupę użytkowników NetBeans, w którym zachęca do udziału i że nie powinniśmy być "hesitate to contact him in case of any questions."

Hello NetBeans fans,

I am happy to announce that since today we are starting to form the NetCAT 6.7 team. If you are interested in taking your part in helping us test NetBeans 6.7 development builds please read more on the homepage [1] and sign up during this week! NetBeans 6.7 feature highlights in short:

* Connected Developer (Kenai integration)
* Maven projects integration
* Ergonomic IDE
* Unified framework for integrating additional languages to the source code editor
* Debugger improvements
* Enhanced C/C++, Groovy, PHP, Ruby support
* and much more...

[1] http://qa.netbeans.org/processes/cat/67

Don't hesitate to contact me in case of any questions.

Best regards,
--
Jiri Kovalsky
Technical Community Manager
http://www.netbeans.org

Zainteresowany/-a sprawdzeniem NetBeansa przed finalnym wydaniem? Chcesz mieć znaczący głos, czy NetBeans jest gotów, czy nie na spotkanie z rynkiem? Zapisz się i wyraź swoją opinię! W zamian można spodziewać się...koszulki..miałem napisać...lepszej funkcjonalnie wersji NetBeans ;-)

17 marca 2009

43. spotkanie Warszawa JUG - "Domain-Driven Design" z Pawłem Lipińskim

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

Temat prezentacji: Domain-Driven Design
Prelegent: Paweł Lipiński

Tworzenie aplikacji jest dziedziną inżynierii o chyba najniższej jakości i przewidywalności tworzonych produktów (no, może poza budownictwem w Polsce). Jednym z powodów tego są dopiero rozwijające się metodyki, techniki i wzorce tworzenia aplikacji. Do tego dochodzi skupienie się programistów głównie nad technologiami, a dużo mniejsze nad dziedziną, której dotyczy aplikacja oraz wewnętrzną strukturą tworzonego systemu. Jednym ze sposobów zaradzenia tej sytuacji jest Domain-Driven Design (DDD). DDD to zbiór wzorców i dobrych praktyk definiujących sposób tworzenia aplikacji biznesowych. Poznanie tych wzorców oraz filozofii stojącej za nimi pomaga skupić się na dostarczaniu wartości oraz tworzyć ładne pod względem architektury aplikacje.

Paweł Lipiński zajmuje się tworzeniem aplikacji od 12 lat, w tym ostatnie 10 głównie w Javie. Ostatnie parę lat mniej skupia się na technologiach a bardziej na technikach sprawnej realizacji projektów. Fanatycznie skupia się na jakości, architekturze kodu i aplikacji. Jest wyznawcą Zwinności, którą stara się propagować i wdrażać gdzie popadnie, łącznie z wychowaniem własnych dzieci :)

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!

16 marca 2009

Próba sił z podcastami

13 komentarzy
A co tam, skoro czytają, to może zechcą i posłuchać?! ;-) Grzegorz Duda jako jeden z pierwszych, znanych mi członków polskiej społeczności javowej zabrał się za wydawanie podcastów w serii Java Polish Podcast. Było ich kilka, ale chwilowo przycichło. Sądzę, że chwilowo, bo po kilku podcastach zagranicznych wzięło i mnie na nagrywanie. Zaraz napisałem do Grzegorza z propozycją wskrzeszenia podcasta i podobnież jedyną zauważalną przeszkodą mogą być tematy i ewentualni goście "słuchowisk":

"Z Radkiem gadaliśmy, ze możnaby na pierwszy ogień wziąć języki na JVM. Tylko w naszej trójce (Grzegorz, Radek i Jacek - przyp. autora) to wszyscy jesteśmy "przesiąknięci" Grailsami i Groovym. Może więc będzie to tylko o Groovy/Grails, a follow up session o innych językach/frameworkach? Zaprosilibyśmy wtedy np. Wiktora (Gworka - przyp. autora) z Rubym itp. Czy wszystko na raz?"

No właśnie, wszystko na raz? Czy musimy ustalać tematyki przyszłych spotkań, czy po prostu do dzieła? Jest tyle ciekawych osób, które zazwyczaj są zarobieni, ale na 5-10 minut pogawędki zawsze znajdą czas. Tak sobie myślę, że to jest właśnie klucz do sukcesu - różnorodność i nieprzewidywalność. Dzwonię do kolesia i zaczynam z nim rozmowę - taką krótką, przypadkową na pewien temat, np. dlaczego Grails, albo wdrożenia Red5 jako platformy do puszczania naszych spotkań jugowych w eter, albo jako przegląd pRaSSówki javowej i taki materiał idzie jako podcast. Tyle się dzieje wokół, że z pewnością znalazłyby się tematy do rozmów i sami rozmówcy. Będzie java4people, będzie GeeCON, będzie Javarsovia 2009, więc już chociażby podcasty jako relacje z nich to seria na 3 kolejne miesiące. A dalej jakoś samo pójdzie - tylko zacząć (i komuś przekazać do prowadzenia ;-))

Zaraz napisałem do kilku osób zajmujących się podcastami o wskazówki. Napisałem również do Glena Smitha stojącego za Grails Podcast, który napisał:

Hi Jacek,

Just a few things. The most important is a decent USB microphone. I use a Snowball which I love - and is popular and available.

To do the mix I use GarageBand on OSX, but you can easily use Audacity on any platform.

For recording over Skype we use Ecamm Call recorder but there's probably lot of options for that.

Hope that gets you started!

Glen.


Już po odpowiedzi od Grześka wiedziałem, że należy się przyjrzeć mp3DirectCut oraz Audacity, więc miałem już za sobą całą serię How to Podcast o podcastach z Audacity w roli głównej. Mam go już u siebie i czeka na testy. Krótkie filmiki o Audacity dają solidne podstawy do rozpoczęcia tematu nagrywania podcastów. Nie pozostaje nic innego, jak tylko zacząć.

A skoro o podcastach i ich zawartośći, może są tematy czy osoby z polskiej sceny javowej, które chcielibyście wysłuchać? Czy w ogóle warto zajmować się podcastami, skoro jest ich już tyle, że wielu osobom i tak czasu nie starcza, aby je wszystkie wysłuchać, a tu proponuje się nowy? Ile miałyby trwać - 15, 30 czy 60 minut? Wiele różnych tematów, czy jeden wiodący i jakieś dodatki? Wskazówki mile widziane. Zamieniam się w słuch...

13 marca 2009

Relacja z rozdziału 6. o mapowaniu URL w Grails z DGG2

2 komentarzy
Lektura książki The Definitive Guide to Grails, Second Edition idzie dalej. Wspominałem już, że wstrzymałem lekturę, aby nadrobić jej relację na blogu, co zmusza mnie do dwu-, a czasem nawet kilkukrotnego wertowania tych samych stron. I dobrze, bo zdecydowanie poprawia zrozumienie tematu. Dodając do tego projekt nauczyciel i mam(y) wszystko potrzebne do poznawania Grails. Zauważyłem również, że Grails znalazł swoje miejsce na innych polskich blogach, więc spragnionych wiedzy grailsowej zapraszam na strony Michała Piotrowskiego (blog Stronnice Chlebika) oraz Mateusza Mrozewskiego (blog Blog IT - Mateusz Mrozewski). Ich styl pisania znacznie odbiega od mojego - jest krócej i bardziej treściwie, i z przykładami (!)

Tym razem przejdziemy do konfiguracji adresów URL w Grails. Rozdział 6. "Mapping URLs" pozwala zrozumieć, w jaki sposób można definiować relację pomiędzy URL a jego kontrolerem i akcją z możliwością przesyłania własnych parametrów.

Pierwsza część URL (po kontekście aplikacji webowej) to nazwa kontrolera, a kolejna (opcjonalna) nazwa jego akcji (domknięcia), np. dla poniższego kontrolera PierwiastekController:
 package pl.jaceklaskowski.nauczyciel

class PierwiastekController {
def index = {
render 'Wywołano index'
}
}
adres http://localhost:8080/nauczyciel/pierwiastek/index to wykonanie jego akcji index. Jeśli nie podano opcjonalnej nazwy akcji, zostanie wykonana akcja domyślna kontrolera. Ostatni, opcjonalny, element URL to identyfikator id.

Definicja składowych URL znajduje się w grails-app/conf/UrlMappings.groovy.

Jeśli korzystamy z opcjonalnych składowych (za pomocą operatora dereferencji w Groovy - ?) w URL to muszą one być na końcu wzorca.

Zmienna to składowa URL poprzedzona $. Mapowanie może zawierać tekst statyczny. Nazwa kontrolera i jego akcji nie muszą występować w URL. Za pomocą bloku static mappings w skrypcie UrlMappings możemy przypisać wykonanie wskazanego kontrolera i jego akcji, np. (Listing 6-3):
 class UrlMappings {
static mappings = {
"/pierwiastek/$id" {
controller = 'pierwiastek'
action = 'show'
}
}
}
Powyższa konfiguracja jest równoznaczna z
 class UrlMappings {
static mappings = {
"/pierwiastek/$id"(controller = 'pierwiastek', action = 'show')
}
}
}
Wybór należy do nas, który ze sposobów bardziej do nas przemawia.

W Grails preferuje się adresy typu /pierwiastek/potas zamiast typowych /pierwiastek?nazwa=potas. Przekazanie wartości "potas" w parametrze nazwa to konfiguracja:
 class UrlMappings {
static mappings = {
"/pierwiastek/$nazwa"(controller = 'pierwiastek', action = 'show')
}
}
}
Dostęp do zmiennej jest możliwy przez zmienną params, bez względu, czy przekazaliśmy parametr przez mapowanie w UrlMappings (jako część URL), czy bezpośrednio jako parametr żądania.

Autorzy proponują korzystać z konwencji "podkreślnik-zamiast-%20", aby adres typu /zwiazek/chlorek wapnia miał postać /zwiazek/chlorek_wapnia zamiast /zwiazek/chlorek%20wapnia. Wystarczy połączyć wcześniejsze mapowanie (z static mappings) z wykonaniem metody replaceAll('_', ' '), aby dostać się do poprawnej wartości parametru, tj.
 class PierwiastekController {
def show = {
def pierwiastek = Pierwiastek.findByNazwa(params.nazwa.replaceAll('_', ' '))
render "Symbol pierwiastka: ${pierwiastek.symbol}, nazwa: ${pierwiastek.nazwa}"
}
}
Poza tym, możliwe jest definiowanie nowych zmiennych żądania, jak gdyby były one w nim przekazane. W ten sposób wykonanie URL rozpoczynającego się /pokazPierwiastek/<nazwa> miało inny skutek niż wykonanie /pokazPierwiastekPelny/<nazwa>. Dla użytkownika końcowego wyróżnikiem jest adres URL, podczas, gdy jego obsługą zajmuje się kontroler, który wywołany zostanie z właściwymi parametrami, tj.
 class UrlMappings {
static mappings = {
"/pokazPierwiastek/$nazwa(controller = 'pierwiastek', action = 'wyswietl') {
format = 'prosty'
}
"/pokazPierwiastekPelny/$nazwa(controller = 'pierwiastek', action = 'wyswietl') {
format = 'pelny'
}
}
}
W ten sposób, obsługa akcji wyswietl w kontrolerze PierwiastekController podejmowałaby decyzję o ilości wyświetlanych danych na bazie parametru format (${params.format}).

Możliwe jest związanie adresu z konkretnym widokiem - stroną GSP. Użyteczne w sytuacji, kiedy widok nie potrzebuje modelu oraz żadna z akcji kontrolera nie musi być wcześniej wykonana. Mapowanie jest identyczne do poprzednich, z kontrolerami i ich akcjami, z tą różnicą, że zamiast action definiujemy parametr view, np.:
 class UrlMappings {
static mappings = {
"/"(view: '/welcome')
}
}
W wyniku zostanie wyświetlona strona grails-app/views/welcome.gsp. Jeśli poza view określimy również parametr controller, wtedy strona GSP będzie tą, która związana jest z danym kontrolerem, np.:
 class UrlMappings {
static mappings = {
"/wyszukaj"(view: 'wyszukaj', controller: 'pierwiastek')
}
}
Dla /wyszukaj zostanie wyświetlona strona grails-app/views/pierwiastek/wyszukaj.gsp *bez* wcześniejszego wykonania akcji w kontrolerze.

Poza tym, możemy w (pod)sekcji constraints nadawać warunki jakie muszą spełniać parametry żądania, aby doszło do wykonania kontrolera, albo wyświetlenia strony, np.:
 class UrlMappings {
static mappings = {
"/pierwiastek/$masaAtomowa(controller = 'pierwiastek', action = 'wyswietl') {
constraints {
masaAtomowa matches: /[0-9]{2}/
}
}
}
}
Mechanizm jest podobny do warunków w klasach dziedzinowych. I podobnie jak w klasach dziedzinowych, wszystkie warunki muszą zajść, aby wykonane zostało mapowanie.

UWAGA: Podobieństwo między warunkami w klasach dziedzinowych, a tymi w mapowaniu URL jest niezwykle subtelne - w klasach dziedzinowych mamy do czynienia ze statyczną zmienną, której przypisujemy domknięcie (jako jej wartość), a w mapowaniu URL wykonujemy metodę constraints przekazując jej parametr będący domknięciem. Uwaga na brak znaku równości. Cuda języka Groovy.

Mapowanie URL pozwala na związanie go z danym adresem URL z użyciem symbolów maski (wieloznacznik) - * (gwiazdka) lub ** (dwie gwiazdki), co oznacza cokolwiek spełniającego warunek bez konieczności przekazywania ciągu spełniającego maskę jako parametr żądania, np.: (Listing 6-14)
 class UrlMappings {
static mappings = {
"/images/*.jpg"(controller:'image')
}
}
co oznacza, że kontroler image jest związany z adresami typu /images/*.jpg, ale już nie z /images/podkatalog/*.jpg, który jest spełniony przy zastosowaniu podwójnej gwiazdki (dowolna hierarchia katalogów). Ciąg znaków spełniający maskę nie będzie związany z żadnym parametrem żądania. Jeśli jednak chcielibyśmy przypisać pasujący ciąg znaków do parametru, poprzedzamy symbol maski nazwą parametru, np.: (Listing 6-16)
 class UrlMappings {
static mappings = {
"/images/$pathToFile**.jpg"(controller: 'image')
}
}
Dla /images/photos/president.jpg parametr pathToFile ma wartość photos/president.

Istnieje możliwość związania różnych akcji kontrolera dla różnych typów żądań HTTP - GET, POST, PUT i DELETE. Zamiast sprawdzać w kontrolerze typ żądania HTTP za pomocą request.method możemy wskazać w action, która akcja kontrolera obsługuje dany typ, np.: (Listing 6-18):
 class UrlMappings {
static mappings = {
"/artist/$artistName" {
controller = 'artist'
action = [GET: 'show',
PUT: 'update',
POST: 'save',
DELETE: 'delete']
}
}
}
Wartością action jest mapa - typ żądania HTTP i akcja kontrolera.

Możemy również wiązać kody odpowiedzi HTTP, np. 404 czy 500 z danym widokiem bądź akcją kontrolera, np.: (Listing 6-20)
 class UrlMappings {
static mappings = {
"404"(controller:'store')
}
}
Domyślnie, Grails obsługuje kod 500 (Internal Error), który wyświetla widok /error (strona grails-app/views/error.gsp), która pozwala na namierzenie błędu w razie...błędu. Zakłada się, że jest użyteczne podczas tworzenia aplikacji i jej debugowaniu. W naszych stronach możemy korzystać z parametru exception (przykładem może być właśnie grails-app/views/error.gsp).

Do tej pory przedstawiony został mechanizm obsługi żądań do własnych URLi. Autorzy przedstawiają sposób tworzenia tych URLi za pomocą znaczników GSP - g:link. Wystarczy dostarczyć niezbędne parametry w atrybucie params wraz z odpowiednio zdefiniowanymi atrybutami action oraz controller, a g:link stworzy właściwy URL, np.: (Listing 6-22)
 class UrlMappings {
static mappings = {
"/showArtist/$artistName"(controller:'artist', action:'show')
}
}
Przy takim UrlMappings wystarczy skonstruować g:link w następujący sposób: (Listing 6-23)
 <g:link action='show'
controller='artist'
params="[artistName:${artist.name.replaceAll(' ', '_')}]">
${artist.name}
</g:link>
Plik UrlMappings można dzielić na mniejsze pliki. Wystarczy, aby miały taką samą strukturę - static mappings, znajdowały się w katalogu grails-app/conf i ich nazwa kończyła się na UrlMappings.

Testowanie własnych mapowań jest możliwe z klasą grails.test.GrailsUrlMappingsTestCase, która rozszerza z kolei klasę groovy.util.GroovyTestCase. Metoda assertForwardUrlMapping sprawdza, czy żądanie do zadanego URL jest obsługiwane przez właściwą akcję kontrolera, np.: (Listing 6-26)
 class ArtistUrlMappingTests extends grails.test.GrailsUrlMappingsTestCase {
void testShowArtist() {
assertForwardUrlMapping('/showArtist/Jeff_Beck',
controller:'artist', action:'display')
}
}
Jeśli mapowanie definiuje własne parametry żądania, np. wcześniej korzystaliśmy z artistName, wtedy sprawdzenie jest możliwe przez przekazanie opcjonalnego domknięcia do metody assertForwardUrlMapping z parametrami i ich żądanymi wartościami, np.
 class ArtistUrlMappingTests extends grails.test.GrailsUrlMappingsTestCase {
void testShowArtist() {
assertForwardUrlMapping('/showArtist/Jeff_Beck',
controller:'artist', action:'display') {
artistName = 'Jeff_Beck'
}
}
}
Sprawdzenie, czy mapowanie dla g:link da oczekiwany rezultat odbywa się za pomocą metody assertReverseUrlMapping, która działa i ma parametry analogiczne do assertForwardUrlMapping.

Sprawdzenie obu przypadków to wykonanie metody assertUrlMapping.

Klasa GrailsUrlMappingsTestCase wczytuje wszystkie mapowania aplikacji. Jeśli chcemy zawęzić zbiór testowanych mapowań, wystarczy zdefiniować w klasie testującej statyczną zmienną mappings, która przyjmuje pojedynczą nazwę klasy mapującej, albo ich listę, np.: (Listing 6-30):
 class ArtistUrlMappingsTests extends grails.test.GrailsUrlMappingsTestCase {
static mappings = [UrlMappings, ArtistUrlMappings]

//...
}
Więcej w dokumentacji Grails w Testing URL Mappings.

12 marca 2009

Grails na Mor.ph

7 komentarzy
Po namowach dałem się przekonać do sprawdzenia usługi Mor.ph rejestrując uruchomienie aplikacji grailsowej nauczyciel.

Jest to pierwsze moje spotkanie z usługą PaaS (ang. Platform as a Service) i nawet nie przypuszczałem, że pójdzie tak gładko. Teraz zdecydowanie łatwiej będzie mnie przekonać do tych nie-javowych rozwiązań dla moich javowych poczynań.

Zacząłem od Java Cookbook (odnośnik w prawym dolnym rogu), a w nim "Preparing a Grails Application for Deployment". Warto przeczytać owe 21 stron (właściwie o samym Grails znacznie mniej, bodajże 2-3 strony), bo opisują nie tylko samą pracę z platformą Mor.ph, ale również sposób konfiguracji dostępu do bazy danych PostgreSQL lub MySQL w Grails. Szybki skan po forum Mor.ph i trafiłem na dwie dyskusje Grails deployment using the Mor.ph plugin oraz Private github and deployment. Z pierwszej dowiedziałem się o Mor.ph Deploy Plugin. Pobrałem plik properties oraz jar (oba z sekcji Java Tools for Morph AppSpace Deployment w panelu administracyjnym) i zgodnie z instrukcją do wtyczki umieściłem je w katalogu głównym projektu.

Następnie instalacja wtyczki morph-deploy.
 jlaskowski@work /cygdrive/c/projs/sandbox/nauczyciel
$ grails install-plugin morph-deploy
Welcome to Grails 1.1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: c:/apps/grails

Base Directory: C:\projs\sandbox\nauczyciel
Running script c:\apps\grails\scripts\InstallPlugin.groovy
Environment set to development
Reading remote plugin list ...
Reading remote plugin list ...
[get] Getting: http://plugins.grails.org/.plugin-meta/plugins-list.xml
[get] To: ...\.grails\1.1\plugins-list-default.xml
.....................
[get] last modified = Thu Mar 12 08:37:25 CET 2009
[get] Getting: http://plugins.grails.org/grails-morph-deploy/tags/RELEASE_0_1/grails-morph-deploy-0.1.zip
[get] To: ...\.grails\1.1\plugins\grails-morph-deploy-0.1.zip
...
[get] last modified = Thu Jan 22 00:21:40 CET 2009
[copy] Copying 1 file to ...\.grails\1.1\projects\nauczyciel\plugins
Installing plug-in morph-deploy-0.1
[mkdir] Created dir: ...\.grails\1.1\projects\nauczyciel\plugins\morph-deploy-0.1
[unzip] Expanding: ...\.grails\1.1\plugins\grails-morph-deploy-0.1.zip
into ...\.grails\1.1\projects\nauczyciel\plugins\morph-deploy-0.1
Executing morph-deploy-0.1 plugin post-install script ...
Plugin morph-deploy-0.1 installed
Plug-in provides the following new scripts:
------------------------------------------
grails deploy
Found events script in plugin morph-deploy
Wtyczka morph-deploy bazuje na wtyczce Ant, która jest po prostu przesłonięta przez Grails (wbudowana w jego architekturę przez wywołanie odpowiedniego skryptu Groovy z pomocą Gant). Analiza źródła wtyczki zdecydowanie poprawia zrozumienie jej działania - %userprofile%\.grails\1.1\projects\nauczyciel\plugins\morph-deploy-0.1\scripts\Deploy.groovy i nie powinna nastręczać problemów nawet dla początkujących.

Kolejny krok to grails war.
 jlaskowski@work /cygdrive/c/projs/sandbox/nauczyciel
$ grails war
Welcome to Grails 1.1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: c:/apps/grails

Base Directory: C:\projs\sandbox\nauczyciel
Running script c:\apps\grails\scripts\War.groovy
Environment set to production
Warning, target causing name overwriting of name startLogging
Removing JDBC 2 Extensions JAR from WAR for Mor.ph deployment
Error executing script War: No such property: stagingDir for class: _Events
gant.TargetMissingPropertyException: No such property: stagingDir for class: _Events
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:329)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)
at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:344)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:334)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy:480)
Caused by: groovy.lang.MissingPropertyException: No such property: stagingDir for class: _Events
at _Events$_run_closure1.doCall(_Events.groovy:6)
at War$_run_closure1.doCall(War.groovy:38)
at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)
... 10 more
No cóż, nie idzie gładko, ale ku dobru ogólnemu znalazłem w tym pewien ukryty cel - nie od dzisiaj wiadomo, że nie ma to jak uczyć się przy dobrej zabawie ;-) Zaglądamy do skryptu _Events.groovy (w katalogu %userprofile%\.grails\1.1\projects\nauczyciel\plugins\morph-deploy-0.1\scripts), gdzie można znaleźć
 Ant.delete(file:"${stagingDir}/WEB-INF/lib/jdbc2_0-stdext.jar")
Już wcześniej, w samym dokumencie Java Cookbook, wspomina się o usunięciu pliku jdbc2_0-stdext.jar:

By default, Grails adds all dependencies in the WEB-INF/lib directory of the war. These dependencies include jdbc2_0-stdext.jar. This jar contains the JDBC 2.0 extensions, but these classes have been included in JDK 1.4 long time ago, and are only needed when running with JDK 1.3. By default, this obsolete jar is still included. This jdbc2_0-stdext.jar must be removed from the WEB-INF/lib directory of the war

ale, przynajmniej dla wersji Grails 1.1, nie jest to już w ogóle potrzebne - plik nie jest dodawany do paczki. Pomogło skasowanie pliku _Events.groovy (= zmieniłem nazwę na X_Events.groovy).

Następnie grails deploy.
 jlaskowski@work /cygdrive/c/projs/sandbox/nauczyciel
$ grails deploy
Welcome to Grails 1.1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: c:/apps/grails

Base Directory: C:\projs\sandbox\nauczyciel
Running script ...\.grails\1.1\projects\nauczyciel\plugins\morph-deploy-0.1\scripts\Deploy.groovy
Environment set to development
Warning, target causing name overwriting of name default
This will deploy the file nauczyciel-0.1.war
Enter your Mor.ph username:
jacek
Enter your Mor.ph password:
****
Please wait...
[java] Uploading the code...
[java] Creating new appspace version...
[java] Deploying the application...
[java] Deploy Done.
[java] For more information on the status of this deployment, you
[java] can view the Deployment Logs by clicking 'Manage' located
[java] on your subscription widget and by clicking the Logs tab.
[java] In this same page, you can also view your Production logs
[java] and Scheduled task logs.
[java] ** transaction commit **
Tym razem wejście na http://nauczyciel.morphexchange.com/ i...

Działa!

Dzięki wszystkim za nieustanne przypominanie...ekhm...życzliwe namawianie, aby w końcu sprawdzić Mor.pha w działaniu! Niewiele trzeba, a jaka radość.

Zmiany oczywiście są już w projekcie nauczyciel w GitHubie. Pomoc przy rozwoju aplikacji mile widziana.

11 marca 2009

Grails w akcji - nie ma to jak prosty, ale własny widok

6 komentarzy
Od jakiegoś czasu wspomninam o moich postępach w lekturze książki "The Definitive Guide to Grails, Second Edition" (DGG2) i zdecydowanie za mało w nich doświadczeń praktycznych. Teoretycznie, po 10-ciu rozdziałach DGG2 i wcześniejszej książce, mógłbym sądzić, że znam Grails od podszewki. Zacząłem nawet przeglądać skrypty dostarczane z Grails, ale wciąż to za mało. Wiedza teoretyczna jak najbardziej wskazana, ale praktyczna pozwala uzmysłowić sobie niuanse, o których mógłbym nawet nie pomyśleć podczas lektury książek. Zresztą, doświadczam tego nierzadko, że mimo dużej liczby artykułów temat przemawia do mnie dopiero wtedy, kiedy zacznę się z nim zmagać praktycznie. Wtedy wszystko wydaje się takie oczywiste.

Kręcę się koło tworzenia aplikacji grailsowej, którą mógłbym obsłużyć kilka tematów - najważniejszy z nich, to narzędzie pomocnicze do nauki dla moich dzieciaków (jak mają coś wkuwać, np. chemia czy angielski, to nie są za bardzo zmotywowane do pracy z książką, a przy kompie to jakoś tak wszystko łatwiej im wchodzi), po drugie jako sposób na pogłębienie wiedzy praktycznej z Grails i ostatni, to po prostu stworzenie aplikacji referencyjnej, którą mógłbym wykorzystać do moich kolejnych wystąpień o Grails (jest jeszcze jeden, a właściwie dwa powody, ale o nich na razie sza).

Pierwszy krok podczas tworzenia aplikacji grailsowej to stworzenie projektu poleceniem grails create-app. Określamy nazwę projektu i po chwili mamy właściwą strukturę katalogową (dobry moment, aby umieścić ją od razu w systemie kontroli wersji - właśnie otworzyłem konto na github, aby nie być gołosłownym i stworzyłem projekt nauczyciel).

Warto przyjrzeć się wynikowi polecenia grails create-app, aby zajrzeć za kulisy Grails.
 jlaskowski@work /cygdrive/c/projs/sandbox
$ grails create-app nauczyciel
Welcome to Grails 1.1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: c:/apps/grails

Base Directory: C:\projs\sandbox
Running script c:\apps\grails\scripts\CreateApp_.groovy
Environment set to development
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\src
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\src\java
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\src\groovy
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\controllers
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\services
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\domain
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\taglib
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\utils
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\views
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\views\layouts
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\i18n
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\conf
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\test
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\test\unit
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\test\integration
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\scripts
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\web-app
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\web-app\js
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\web-app\css
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\web-app\images
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\web-app\META-INF
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\lib
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\conf\spring
[mkdir] Created dir: C:\projs\sandbox\nauczyciel\grails-app\conf\hibernate
[propertyfile] Creating new property file: C:\projs\sandbox\nauczyciel\application.properties
[copy] Copying 1 resource to C:\projs\sandbox\nauczyciel
[unjar] Expanding: C:\projs\sandbox\nauczyciel\grails-shared-files.jar into C:\projs\sandbox\nauczyciel
[delete] Deleting: C:\projs\sandbox\nauczyciel\grails-shared-files.jar
[copy] Copying 1 resource to C:\projs\sandbox\nauczyciel
[unjar] Expanding: C:\projs\sandbox\nauczyciel\grails-app-files.jar into C:\projs\sandbox\nauczyciel
[delete] Deleting: C:\projs\sandbox\nauczyciel\grails-app-files.jar
[move] Moving 1 file to C:\projs\sandbox\nauczyciel
[move] Moving 1 file to C:\projs\sandbox\nauczyciel
[move] Moving 1 file to C:\projs\sandbox\nauczyciel
Installing plug-in hibernate-1.1
[mkdir] Created dir: C:\Documents and Settings\jlaskowski\.grails\1.1\projects\nauczyciel\plugins\hibernate-1.1
[unzip] Expanding: C:\Documents and Settings\jlaskowski\.grails\1.1\plugins\grails-hibernate-1.1.zip
into C:\Documents and Settings\jlaskowski\.grails\1.1\projects\nauczyciel\plugins\hibernate-1.1
Executing hibernate-1.1 plugin post-install script ...
Plugin hibernate-1.1 installed
Created Grails Application at C:\projs\sandbox/nauczyciel
Warto nadmienić, że polecenie grails to tak naprawdę skrypt napisany w Gant, czyli nakładce Groovy dla Ant. Wiedza jaką mamy odnośnie Ant jest wystarczająca, aby zrozumieć działanie Gant. Dodając do tego znajomość Groovy i mamy komplet. Każde polecenie grails ma swój skrypt. Grails przeszukuje właściwe katalogi w poszukiwaniu skryptu i w przypadku create-app będzie to $GRAILS_HOME/scripts/CreateApp_.groovy. Można do niego zajrzeć, aby poznać, jak niewiele trzeba było, aby stworzyć Grails (można powiedzieć, że wszystko było, tylko należało dopasować do siebie części - Spring, Hibernate, Groovy, teraz Ant i kilka innych klocków). Pierwsza linia tego skryptu to
 includeTargets << grailsScript("_GrailsCreateProject")
która (jak łatwo się zorientować) dołącza zawartość kolejnego skryptu _GrailsCreateProject.groovy i ustawia zadanie createApp jako domyślne. Kiedy zaczniemy przeglądać skrypt dalej dojdziemy do zadania createStructure (przez initProject) , który znajduje się w _GrailsInit.groovy i przedstawia się następująco:
 target(createStructure: "Creates the application directory structure") {
ant.sequential {
mkdir(dir: "${basedir}/src")
mkdir(dir: "${basedir}/src/java")
mkdir(dir: "${basedir}/src/groovy")
mkdir(dir: "${basedir}/grails-app")
mkdir(dir: "${basedir}/grails-app/controllers")
mkdir(dir: "${basedir}/grails-app/services")
mkdir(dir: "${basedir}/grails-app/domain")
mkdir(dir: "${basedir}/grails-app/taglib")
mkdir(dir: "${basedir}/grails-app/utils")
mkdir(dir: "${basedir}/grails-app/views")
mkdir(dir: "${basedir}/grails-app/views/layouts")
mkdir(dir: "${basedir}/grails-app/i18n")
mkdir(dir: "${basedir}/grails-app/conf")
mkdir(dir: "${basedir}/test")
mkdir(dir: "${basedir}/test/unit")
mkdir(dir: "${basedir}/test/integration")
mkdir(dir: "${basedir}/scripts")
mkdir(dir: "${basedir}/web-app")
mkdir(dir: "${basedir}/web-app/js")
mkdir(dir: "${basedir}/web-app/css")
mkdir(dir: "${basedir}/web-app/images")
mkdir(dir: "${basedir}/web-app/META-INF")
mkdir(dir: "${basedir}/lib")
mkdir(dir: "${basedir}/grails-app/conf/spring")
mkdir(dir: "${basedir}/grails-app/conf/hibernate")
}
}
Właśnie to zadanie najbardziej uzmysławia, że to co się dzieje pod spodem create-app, to nic innego jak stary, dobry Ant przesłonięty Groovy DSL. Zero magii (która towarzyszyła mi na początku poznawania Grails).

Następnie pora na stworzenie klasy dziedzinowej poleceniem grails create-domain-class pl.jaceklaskowski.nauczyciel.Pierwiastek. Zazwyczaj polecenia do tworzenia klas w Grails nie zawierają opcjonalnego pakietu. Postanowiłem zmienić ten zwyczaj (u siebie przynajmniej). Zalet nadawania pakietów do naszych klas nie ma co przedstawiać - ochrona przez potencjalnym konfliktem z tą samą klasą z innego projektu. Do klasy dodaję trzy atrybuty - symbol, nazwa i opis. Warto w tym momencie skorzystać z jakiegoś IDE - ja korzystam z NetBeans 6.7 (wersja z dzisiaj).
 package pl.jaceklaskowski.nauczyciel

class Pierwiastek {

String symbol
String nazwa
String opis

}
Mamy dziedzinę (model). Pozostaje stworzyć dla niej kontroler. Możemy tak - grails create-controller pl.jaceklaskowski.nauczyciel.Pierwiastek, albo podpieramy się narzędziami w IDE (na węźle Controllers wybieramy New > Grails Controller...).
 package pl.jaceklaskowski.nauczyciel

class PierwiastekController {

def index = { }
}
Teraz mamy kilka możliwości - albo kasujemy domknięcie def index i definiujemy def scaffold = Pierwiastek, co włączy dynamiczne rusztowanie (ang. scaffolding), albo tworzymy widok, który będzie odpowiadał domknięciu index (w przeciwnym przypadku skończymy z 404 jak w Wystąpienia publiczne jako utrwalenie wiedzy z Grails w tle). Najlepiej byłoby połączyć oba rozwiązania i wiem, że się da, ale raz, że nie pamiętam dokładnie jak, a dwa, że nie będę teraz aż tak kombinował. Wybieram stworzenie widoku dla akcji index. I ponownie mamy kilka opcji - albo grails generate-views pl.jaceklaskowski.nauczyciel.Pierwiastek, albo podparcie się IDE do stworzenia domyślnej, pustej funkcjonalnie strony grails-app/views/pierwiastek/index.gsp. Wybieram pierwsze podejście (jeszcze z niego nie korzystałem publicznie). W katalogu grails-app/views/pierwiastek powinny pojawić się 4 pliki, które odpowiadają każdej z akcji CRUD:
 $ ls grails-app/views/pierwiastek/
create.gsp edit.gsp list.gsp show.gsp
Ale moment! Nie ma index.gsp?! Zatem nie pozostaje mi nic innego jak wrócić do stworzenia jej ręcznie. Z pomocą NetBeans otrzymuję następującą stronę grails-app/views/pierwiastek/index.gsp:
 <%@ page contentType="text/html;charset=UTF-8" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Lista pierwiastków</title>
</head>
<body>
<h1>Na razie niewiele, ale początek już mamy...</h1>
</body>
</html>
Uruchomienie aplikacji poleceniem grails run-app i chwila na zastanowienie, co dalej.

Aktualna wersja aplikacji znajduje się w repozytorium git jako projekt nauczyciel. Zainteresowanych udziałem w tym mini-projekcie zapraszam do kontaktu. Najgorzej będzie ze stylami (rozmieszczę div'y, ale pewnie nic poza tym), więc jeśli masz zmysł plastyczny i potrafisz stworzyć właściwe CSSy koniecznie musimy obgadać temat współpracy.

10 marca 2009

Relacja z rozdziału 5. o widokach w Grails z DGG2

2 komentarzy
Rozdział 5. z książki "The Definitive Guide to Grails, Second Edition" dotyczy warstwy widoków w Grails. Około 40 stron naszpikowane wiadomościami dotyczącymi Groovy Server Pages (GSP). W zasadzie jestem już przy 10. rozdziale i już myślałem, że kompletnie zarzucę relacje z lektury książki ze względu na ich obszerność, ale ostatnie wydarzenia - spotkanie Warszawa JUG oraz konferencja 4Developers - upewniły mnie, że nie ma lepszego sposobu na utrwalenie wiedzy, jak właśnie jej przedstawienie na blogu (zawsze zmusza mnie do ponownego przeczytania rozdziału przynajmniej jeszcze raz). Z każdym rozdziałem ta książka upewnia mnie w przekonaniu, że jest po prostu lekturą obowiązkową każdego szanującego się Grailsowca. W międzyczasie próby z mini-aplikacją Nauczyciel w Grails i kolejne wystąpienia o Grails będą wyłącznie w stylu warsztatów (a ja nie dam się wystawić do wiatru, gdzie nic nie działa).

W Java EE 5 mamy JavaServer Pages (JSP) jako technologię widoku, a w Grails mamy Groovy Server Pages (GSP). Po co nam nowe? Z jednej przyczyny - wykorzystanie siły Groovy. Jeśli ktokolwiek próbował swoich sił w JSP czy JSF, a w szczególności w kontekście tworzenia własnych znaczników, to pamięta ile potrzeba było czasu na ich stworzenie. W GSP będzie inaczej, zdecydowanie prościej.

W GSP mamy do dyspozycji kilka atrybutów: application (egzemplarz ServletContext), flash, out (egzemplarz java.io.Writer), params (mapa parametrów żądania), request (HttpServletRequest), response (HttpServletResponse) oraz session (HttpSession).

GSP wspiera konstrukcje JSP z kilkoma własnymi rozszerzeniami. W właśnie wydanej wersji Grails 1.1 jest nawet pełne wsparcie dla znaczników JSP.

Grails jest implementacją wzorca MVC. Zgodnie z zasadami MVC, widok wyrysowywuje przekazany model. W Grails przekazanie modelu następuje z kontrolerów poprzez zwrócenie mapy z akcji, np.
 class PojecieController {
def zapytaj = {
def listaPojec = Pojecie.withCriteria {
...
}
[pojecia:listaPojec]
}
}
Dla zwrócenia uwagi: ostatnia instrukcja [pojecia:listaPojec] jest równoważna zwróceniu mapy, gdzie pod kluczem pojecia znajdziemy listę listaPojec.

Wynik wykonania akcji określa model, na którym pracuje widok i poszczególne elementy mapy są dostępne jako zmienne na stronie, jak inne atrybuty.

GSP wspiera podzbiór dyrektyw JSP - instrukcji, które występują na początku strony i określają jej działanie, np. ustawiają typ zawartości odpowiedzi - HTML, XML, "czysty" tekst - (dyrektywa <%@ page contentType %>) lub po prostu importują klasy.

W GSP możemy umieścić niewielkie kawałki dowolnego kodu grailsowego ("wstawki") między <% a %>, podobnie jak w JSP. Bardzo interesującym przykładem może być 3-krotne wyświetlenie napisu (Listing 5-4):
 <% 3.times { %>
<p>Trzykrotnie wyświetlony napis</p>
<% } %>
Dla ludzi spoza świata technologii javowych wstawki mogą przypominać RHTML, jednakże są bardziej skierowane do naszych przyzwyczajeń z JSP i dokładnie z tego samego powodu nierekomendowane na rzecz znaczników GSP.

Mamy również do dyspozycji konstrukcje <%= "tekst do wyświetlenia" %>, które zastępują konstrukcje <% out << "tekst do wyświetlenia" %>.

Różnicą między GSP a JSP jest mechanizm wbudowanych GStringów (znanych użytkownikom JSF chociażby z facelets), tj. konstrukcji ${ziarno.atrybut}. Jak to zostało w książce ujęte, GSP to jeden wielki GString, czyli mechanizm podstawiania wartości znane z Groovy w zmiennych będących ciągami znaków dotyczą i GSP, np. ${album?.title?.toUpperCase()} z użyciem operatora warunkowego ? (który przy poprzedzającej pustej wartości lub null nie wykona kolejnych metod odczytu).

Podobnie jak JSTL dla JSP, Grails posiada własną bibliotekę wbudowanych znaczników do typowych konstrukcji programistycznych - pętle, instrukcja wyboru (switch), instrukcje warunkowe (if). Każdy z wbudowanych znaczników należy do przestrzeni g i, w przeciwieństwie do JSP, nie ma konieczności importu tych, albo innych znaczników. Po prostu są widoczne i tyle. Dyrektywa taglib z JSP jest wspierana.

<g:set var="zmienna" value="wartość" /> ustawia zmienną lokalną dla strony o danej wartości. Wartość może być dowolną konstrukcją typu ${}. Opcjonalny atrybut scope określa zasięg widoczności zmiennej - application (cała aplikacja), session (sesja użytkownika), flash (to i kolejne żądanie), request (jedynie na czas trwania aktualnego żądania), page (czas wyrysowywania strony).

Znacznik g:each pozwala na dostęp do aktualnej wartości zmiennej przez it (jak w domknięciach, którymi tak na prawdę są znaczniki), lub po nazwie określonej parametrem var, np. <g:each var="song" in="${album.songs?}">. Podobnie będzie z innymi znacznikami iterującymi, jak np. g:collect.

Ograniczanie wartości, po których iterujemy, możliwe jest w g:findAll przez warunek logiczny w atrybucie expr, który jest wyliczany, dla każdego elementu w liście (atrybut in).

Ciekawostką GSP są konstrukcje typu ${it.songs?.title}, gdzie title jest atrybutem elementu w liście wskazanej przez it.songs (songs jest po prostu kolekcją elementów z danym atrybutem), a samo it jest aktualnym elementem na liście w pętli głównej (w domknięciu).

Dynamiczne znaczniki w Grails są domknięciami w klasach będących bibliotekami znaczników, które kończącą się przyrostkiem TagLib w katalogu grails-app/taglib. Nie ma znaczenia, czy znacznik jest nasz, czy dostarczany przez Grails - wszystkie domyślnie należą do przestrzeni "g". Dodatkowo, możemy je wywoływać jak metody we wstawkach GSP i wyrażeniach GString. Nabiera to znaczenia podczas tworzenia odpowiedzi będącymi poprawnymi dokumentami XML. W takim przypadku niemożliwe jest umieszczenie znacznika jako wartość atrybutu innego, np. zamiast (Listing 5-15)
 <a href="<g:createLink action="list" />">Odnośnik ("sznurek")</a>
który nie jest poprawną konstrukcją w XML możemy to zapisać jako
 <a href="${createLink(action:'list')}">Odnośnik ("sznurek")</a>
który jest już poprawny w XML.

Znacznik g:link ma 4 atrybuty: controller, action, id oraz params. Poza id, wszystkie są oczywiste. Atrybut id służy do przekazania identyfikatora, który wskaże na interesujące nas dane w klasie dziedzinowej. Jeden z atrybutów controller i action jest obowiązkowy. Brak controller wskazuje na aktualnie uruchomiony kontroler, a brak action to wskazanie na domyślną akcję danego kontrolera. Inne znaczniki "akcyjne", np. g:createLink czy g:form posiadają podobne atrybuty i ich znaczenie jest identyczne, tj. wskazują na kontroler i jego akcję.

Znacznik g:datePicker służy do obsługi pól typu java.util.Date.

Znacznik g:hasErrors umożliwia sprawdzenie, czy ze wskazanym przez atrybut bean ziarnem (a w zasadzie klasą dziedzinową) nie są związane jakiekolwiek błędy poprawności danych. Wyświetlenie błędów w bardzo podstawowej postaci to funkcja znacznika g:renderErrors.

Obsługa stronicowania to zadanie dla znacznika g:paginate.

Utworzenie własnej biblioteki znaczników to wykonanie polecenia grails create-taglib <nazwa-biblioteki>. Dowolne domknięcie to znacznik, który przyjmuje 2 parametry wejściowe - attrs i body. Parametr attrs (typ java.util.Map) jest mapą atrybutów, a body odpowiada treści znacznika (jako kolejne domknięcie). Skoro body jest domknięciem możemy wywołać je jako funkcję. W ten sposób możemy skorzystać z parametru it w ramach naszego znacznika, który będzie odpowiadał parametrowi wejściowemu domknięcia body.

Jak wszystkie znaczniki GSP i nasze będą należały do przestrzeni g chyba, że zadeklarujemy statyczną zmienną namespace w klasie biblioteki znaczników, np. (Listing 5-43):
 class PojecieTagLib {

static namespace = 'pj'

def wyswietl = { attrs, body ->
body(attrs.napis)
}
}
Warto zauważyć, że prostota tworzenia znaczników pozwala na ich dynamiczne przeładowywanie podczas tworzenia aplikacji. Nie potrzeba żadnych wyrafinowanych sztuczek. Po prostu zapis i Grails zadba o aktualność znacznika.

W testowaniu znaczników pomaga nam klasa grails.test.TagLibUnitTestCase. Rozszerzamy ją i w metodzie testującej wywołujemy metody na zmiennej tagLib, która odpowiada testowanej bibliotece znaczników (Grails rozpoznaje, o którą bibliotekę chodzi przez nazwę klasy testującej - nazwa biblioteki znaczników rozszerzona o przyrostek Tests). Metoda-znacznik - przyjmuje atrybuty w postaci klucz:wartość, a body przekazywane jest jako domknięcie, np. (Listing 5-44):
 class PojecieTagLibTests extends TagLibUnitTestCase {
void testRepeat() {
tagLib.wyswietl(napis: 'Tekst do wyświetlenia') {
'Treść znacznika: ${it}'
}
}
assertEquals 'Treść znacznika: Tekst do wyświetlenia', tagLib.out.toString()
}
Atrybut out to wyjście znacznika, więc sprawdzenie go z oczekiwanym przez nas wynikiem to sprawdzenie jego poprawnego działania.

Tyle w związku ze znacznikami GSP, co i tak stanowi niezłą lekturę samą w sobie (a miało być relacją, a jest raczej streszczeniem). Kolejny aspekt GSP to budowa stron z użyciem szablonów i układów.

Szablon GSP jest (wbrew temu co nazwa mogłaby sugerować) specjalnym plikiem GSP, który zawiera jedynie fragment strony. Jego celem jest wyniesienie pewnych elementów strony, do ponownego użycia w innych. Jest to po prostu umieszczenie treści w innym pliku, przez co nasza aplikacja zyskuje na elastyczności. Specjalność szablonu GSP ujawnia się jedynie po nazwie pliku, który musi rozpoczynać się od podkreślnika (_). Za pomocą znacznika g:render wyrysowujemy szablon, który wskazujemy przez atrybut template. Nazwa szablonu jako wartość atrybutu template nie może zawierać podkreślnika, np.
 <g:render template="/pojecie/listaPojec" />
odpowiada szablonowi w pliku grails-app/views/pojecie/_listaPojec.gsp.
Określenie modelu do wykorzystania w szablonie możliwe jest przez atrybut model, który przyjmuje mapę. W książce autorzy zaprezentowali szablon do wyświetlenia listy artykułów i wykorzystali go do wyświetlenia 5-ciu najlepiej sprzedających się albumów, piosenek i artystów, ale można ten sam szablon wykorzystać do wyświetlenia dowolnej ich listy. Niekwestionowany zysk.

Właśnie dzisiaj wyszedł Grails 1.1, co z Groovy 1.6 daje solidne podstawy do budowania naszych dynamicznych aplikacji webowych. Nie zapomnijcie wykonać aktualizacji.

Dodatkowo mam do wręczenia pojedynczą wejściówkę na GeeCONa w Krakowie w dniach 7-8 maja. Będę tam prezentował Apache OpenEJB i nowe funkcjonalności EJB 3.1 i już dumam jakby połączyć go z Grails, albo przynajmniej z Groovy ;-) Tak czy owak, muszę znaleźć sposób, aby wskazać tego szczęśliwca, który otrzyma wejściówkę jako czytelnik Notatnika (aby mógł chociażby zobaczyć mnie w akcji). Pomysły?

07 marca 2009

Wrażenia po krakowskim 4Developers

17 komentarzy
Kto nie był niech żałuje. Byłem jedynie od 3-ciej (przyjechałem do Krakowa z rodziną i z nią spędziłem czas od rana), ale i tak dało się odczuć niezwykły klimat z (podobno) około 400-500 uczestnikami (!) Udało mi się porozmawiać z załogą lubelską ze Sławkiem Sobótką na czele, Grzegorzem Dudą, Markiem Klisiem oraz organizatorami (Anią i Andrzejem). Wysłuchałem prezentacji Waldiego o CEPie, Cometd (=Reverse Ajax) i ESB, i zaczęła się moja tura.

Prezentacja miała dotyczyć nie tylko Grails, ale również Project Zero, a w szczególności produktowi na jego bazie - IBM WebSphere sMash, ale ku mojemu zdumieniu nie tylko, że nie skończyłem prezentacji (i tym samym nie dotarłem do sMasha), ale również żaden z przykładów niezwykle trywialnych, mających na celu rozgrzać publikę nie zadziałał! Wciąż jeszcze nie mogę wyjść ze zdumienia, jak to się mogło stać. Jeśli tak dalej pójdzie, to kolejna prezentacja o Grails chyba w ogóle się nie powiedzie. Pierwsza, podczas wtorkowego spotkania Warszawa JUG, również nie obyła się bez niespodzianki, którą jednak szybko udało mi się spacyfikować (opis problemu znajduje się w Wystąpienia publiczne jako utrwalenie wiedzy z Grails w tle). Dzisiejsza zakończyła się "zerowym" zaprezentowaniem Grails w działaniu (i nie chodzi mi wcale o nawiązanie do Project Zero). Czy mam z tego wnioskować, że nie dane mi jest właściwie przedstawić moc Grails? A tkwi w nim coś faktycznie lekkiego, co sprawia, że Grails rekomendowałbym do każdego z dowolnych projektów webowych. Na pewno byłby znaczącym odświeżeniem technologicznym, a i czas potrzebny do ich realizacji z pewnością byłby krótszy (w porównaniu do innych znanych mi szkieletów webowych). Oczywiście, nie wspominam tutaj nic o często kluczowej wydajności rozwiązania - to pozostawiam zespołom projektowym i ich ewaluacji Grails. Wydajność kompletnego rozwiązania, bez względu na wybór technologii do jego realizacji, *zawsze* wymaga przeprowadzenia testów wydajnościowych. Dla jednego rozwiązania Grails będzie niewystarczający, a dla innego w sam raz. Często również i tak bywa, że aspekt wydajnościowy ma drugorzędne znaczenie, a ważniejszy jest czas realizacji rozwiązania w czym Grails z pewnością nie odstępuje od innych, podobnych szkieletów aplikacyjnych (jeśli nie jest liderem).

Na zakończenie nadmienię, że w mojej prezentacji wzięło udział około 70 osób, ale na moje pytanie o jakąkolwiek znajomość Groovy i Grails - praktyczną, albo taką z artykułów - podniosło rękę 7-10 osób. Czy to wyjaśnia niewielkie zainteresowanie piątkowymi warsztatami Groovy na 4Developers? Może cena warsztatów (która przy 1000PLN może być zaporowa) albo po prostu jeszcze niewielkie wykorzystanie Groovy i Grails? A może potrzeba rozruszać towarzystwo warsztatami Grails podczas Javarsovia 2009? Wystarczy po prostu 2-3h bez slajdów, a jedynie praca z Grails i IDE. Najpierw tworzenie aplikacji zgodnie z pewnym planem (ustalonym przez prowadzącego), a następnie wdrażanie funkcjonalności od publiczności. To mogłoby być interesujące doświadczenie dla obu stron. Pisze się ktoś? I niekoniecznie musiałoby dotyczyć wyłącznie Grails.

Moja prezentacja z 4Developers dostępna jest jako Zwinne i lekkie aplikacje webowe z Groovy, Grails i Project Zero.

05 marca 2009

Trzeci w Bloger 2008 Roku

8 komentarzy
Dzisiaj miałem zaszczyt wziąć udział w gali finałowej konkursu Bloger 2008 Roku w siedzibie "Polityki". Dzięki Wam zająłem 3. miejsce! Dziękuję!

04 marca 2009

Wystąpienia publiczne jako utrwalenie wiedzy z Grails w tle

10 komentarzy
Podczas wczorajszej prezentacji Grails na 42. spotkaniu Warszawa JUG popełniłem jeden błąd, któremu już więcej się nie dam - mam rozwiązanie i to z pomocą osoby z sali. Jest to jeden z powodów, dla którego warto wystawić się na publiczną krytykę. Przed prezentacją miałem wiele obaw, czy jestem odpowiednio przygotowany do jej poprowadzenia, ale teraz po niecałych 2h wystąpienia czuję się znacznie mocniejszy i występ na 4Developers będzie jak bułka z masłem. Podczas spotkania miałem prawie 120 minut, a 4Developers to jedynie 60 minut, więc jedynie (?) to będzie wyzwaniem - sam Grails już nie powinien. Przynajmniej nie w zakresie, który przedstawię.

Zacząłem prezentację Grails od omówienia jego składowych - klasy dziedzinowe, kontrolery, strony GSP i wzmianką o klasach usługowych, pomocniczych i znacznikach GSP. Na poparcie moich słów postanowiłem zaprezentować przykładową aplikację bez użycia IDE, jedynie z użyciem polecenia grails.

Najpierw grails help dla rozruszania publiczności czymś działającym. Później grails create-app nauczyciel, a następnie grails create-domain-class pojecie i grails create-controller pojecie. Miałem pustą klasę dziedzinową oraz domyślny kontroler, który wyglądał tak:
 class PojecieController {
def index = { }
}
Sądząc, że mam wszystko podszedłem do uruchomienia aplikacji z grails run-app.
 $ grails run-app
Welcome to Grails 1.1-RC2 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: c:/apps/grails

Base Directory: C:\projs\sandbox\nauczyciel
Running script c:\apps\grails\scripts\RunApp.groovy
Environment set to development
...
Running Grails application..
Server running. Browse to http://localhost:8080/nauczyciel
Otwieram przeglądarkę pod adresem http://localhost:8080/nauczyciel, wybieram jedyny dostępny kontroler PojecieController i...bum!

Trochę mnie to wprawiło w zakłopotanie, szczególnie, że to dopiero początek moich programistycznych wyczynów z Grails podczas prezentacji (!) Wcześniej, jakimś cudem, nie przytrafiło mi się to. Na sali było około 50 osób i na moje pytanie o jakiekolwiek (nawet minimalne - samo czytanie o nim) doświadczenia z Grails rękę podniosła wyłącznie jedna osoba (nie czytają mojego bloga?!). Byłem w tarapatach.

Sądząc, że to brak *czegoś* zabrałem się za podobną aplikację z użyciem NetBeans 6.7. Nie trwało długo zanim okazało się, że nie jest lepiej - znowu 404. Chwila zadumy (a wszyscy z sali patrzą) i mam rozwiązanie! Wszystko za sprawą cudownej zasady konwencja-zamiast-konfiguracji (ang. CoC - Convention over Configuration).

I teraz pytanie do czytelników: Dlaczego otrzymałem błąd 404? Jako podpowiedź napiszę, że wszystkie dane do rozwiązania problemu są w tym wpisie. Trochę logicznego myślenia i znalezienie rozwiązania stanie się banalne (poodbnie jak te dowody twierdzeń z wykładów analizy matematycznej ;-)).

03 marca 2009

42. spotkanie Warszawa JUG - "Zwinne i lekkie aplikacje webowe z Grails"

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

Temat prezentacji: Zwinne i lekkie aplikacje webowe z Grails
Prelegent: Jacek Laskowski

Grails jest platformą aplikacyjną, która pretenduje do miana rozwiązania lekkiego i dynamicznego ala Ruby on Rails czy Django. Podejście oparte na zasadach "konwencji-ponad-konfigurację", DRY, dynamiczne i statyczne rusztowanie oraz uproszczone testowanie składowych aplikacji do tej pory zarezerwowane były dla szkieletów webowych spoza obszaru Javy. Z nastaniem ery języków skryptowych uruchamianych na platformie JVM możliwe jest wprowadzenie ich do naszego arsenału projektowego. Można śmiało postawić tezę, że Grails to aplikacja wzorcowa dla Groovy, Spring Framework i Hibernate, więc pozostaje tylko zapytać, czego nie można w nim zrobić, co jest dostępne w innych środowiskach.
Przedstawienie cech Grails podczas prezentacji będzie wspierane jednoczesnym tworzeniem niewielkiej (przykładowej) aplikacji webowej z najnowszą wersją Grails 1.1RC2 oraz wersją rozwojową NetBeans IDE 6.7.

Jacek Laskowski zajmuje się Javą i jej korporacyjną wersją Java EE od pierwszych dni ich pojawienia się na rynku. Utrzymuje swoją wiedzę informatyczną dzieląc się nią wszem i wobec - na blogu, artykułach, wystąpieniach publicznych czy przypadkowych spotkaniach - sądząc, że ciągłe wystawianie swojej wiedzy na publiczną krytykę jest jednym z nielicznych sposobów na jej szybkie wywindowanie w górę. "Pomoc" rodzinna - żona i dwójka dzieciaków - pomaga utrzymać pułap i za daleko nie odlecieć w chmury.

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

Wstęp wolny!

Zapraszam w swoim imieniu i grupy Warszawa JUG!

01 marca 2009

Ergonomic IDE == Fitness Forever == NetBeans Features on Demand

3 komentarzy
Już podczas zabezpieczania aplikacji webowej ze Spring Security z rozwojową wersją NetBeans 6.7 (patrz Proste uwierzytelnianie i autoryzacja w aplikacji webowej ze Spring Security w 15 minut) zaskoczyło mnie pewne zachowanie NetBeans, które polega na włączaniu jego funkcjonalności na żądanie - należy wcisnąć przycisk Activate i żądana funkcjonalność zostanie włączona. Tak było ze wsparciem dla Java EE (we wskazanym artykule), a dzisiaj zauważyłem, że podobnie jest ze wsparciem dla projektów grailsowych i w ogóle dla obsługi Groovy przez NetBeans. Kiedy tworzyłem nowy projekt na moją prezentacje Grails na nadchodzce spotkanie Warszawa JUG (to już w nadchodzący wtorek, 3 marca o 18-tej na MIMUWie!), pojawił się ponownie ekran z Activate.

Wystarczy wcisnąć Activate, aby po chwili móc dalej kontynuować pracę.

Okazuje się, że ma to swoją nazwę - Ergonomic IDE, również nazywane Fitness Forever, albo nawet NetBeans Features on Demand. Zainteresowanych odsyłam do dokumentu NetBeans 6.7M2's New and Noteworthy, w którym opisana jest ta cecha. Podobno ma być coraz szybciej i to przy zachowaniu pełnej wszechstronności NetBeans (!) Zobaczymy. Jak na razie nie zauważyłem specjalnego przyspieszenia jego działania (należę do grupy zadowolonych z aktualnej szybkości jego działania), ale ufam, że pewnie jakieś jest.

Krótkie sprawdzenie funkcjonalności wsparcia projektów z Grails i NetBeans odświeża projekty grailsowe bez problemów. Pamiętam, że właśnie to był główny powód, dlaczego przeniosłem się chwilowo do IntelliJ IDEA 8. Wracam tym samym do dziennych wersji NetBeans 6.7 i prezentacja na spotkaniu Warszawa JUG będzie właśnie z nim.