Pokazywanie postów oznaczonych etykietą gwt. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą gwt. Pokaż wszystkie posty

15 lipca 2008

Słów kilka o Maven 2 w NetBeans 6.5, Sun Certified NetBeans IDE Specialist oraz "GWT w praktyce" Power Netu

4 komentarzy
Piotr Pietrzak w komentarzu do Klasyfikatory w Maven 2 oraz polonizacja NetBeans IDE odpowiedział na wczorajsze moje bolączki związane z brakiem funkcjonalności NetBeans IDE odpowiadającej eclipsowej wtyczce do obsługi projektów mavenowych - m2eclipse w postaci...filmu (!) Wspaniała forma dyskusji w Sieci. Wierzę, że będą kolejne. Tylko, dlaczego nie ma głosu?! ;-)

Temat pobrania źródeł do zależności projektu mavenowego w NetBeans sprowadza się do Add local sources pod prawym przyciskiem myszki dla wybranej zależności

bądź po prostu Download All Library Sources na węźle Libraries w projekcie.

Co ciekawe, po pobraniu wszystkich źródeł, nazwy plików udekorowane są ikonką ze słoikiem i pakunkiem.

Natrafiłem przy okazji na inną ciekawostkę związaną ze wsparciem projektów mavenowych przez NetBeans 6.5 we współpracy z wtyczką Mevenide-NetBeans - wsparcie dla edycji pom.xml. Co ja będę się rozpisywał, sam zobacz (tym razem w postaci zrzutów ekranu, ale może kolejnym razem będzie bardziej filmowo?!). W lokalnym repozytorium mam

a w edytorze jako podpowiedź otrzymuję (Ctrl+Spacja)

Miła niespodzianka, chociaż zanim mnie mile zaskoczyło nie mogłem doczekać się zaindeksowania repozytoriów mavenowych. Dobrze, że cała operacja odbyła się w tle.

Podczas moich dzisiejszych wyczynów programistycznych z NetBeans IDE 6.5M1 potrzebowałem otworzyć klasę w projekcie i jako, że nie jest to Eclipse Ctrl+Shift+T nie działa...domyślnie. W takich przypadkach wspieram się zawsze pomocą Google, ale tym razem miałem wszystko pod ręką, lokalnie. Help > Keyboard Shortcuts Card

po którym otwiera się dokument pdf ze skrótami. Wystarczyło Ctrl+F (szukaj), wpisanie ciągu type, <Enter> i mam - Ctrl-O/Alt-Shift-O Go to type/file. Nie mogłem oprzeć się, aby nie sprawdzić, czy funkcjonalność znana mi z Eclipse dostępna jest i w NetBeans - wyszukiwanie typów po ich skrótach, np. NullPointerException to NPE, albo NoClassDefFoundError to NCDFE. To również jest w NetBeans! Miło.

Tylko jedno mi doskwiera teraz - dlaczego eclipsowe Ctrl+O w edytorze Java to Ctrl+Shift+F12 w NetBeans?! Nic nie przychodzi mi do głowy, aby podmienić to jakoś sensownie, ale sądzę, że to jedno z bardziej użytecznych funkcji IDE - wyświetlenie elementów typu i możliwość przejścia do wybranego, więc należy się coś bardziej ludzkiego. Może jednak warto zmienić mapowanie klawiszy na eclipsowe? Mam takie skrzywienie uniksowe, gdzie edytuję pliki w vi, podczas gdy na linii komend korzystam z trybu Emacs. Pewnie podobnie będzie z klawiaturą w NetBeans. Jak się człowiek do czegoś przyzwyczai, to trudno mu się oderwać od tego.

Na grupie NetCAT 6.5 Jirka (głównodowodzący programem) poprosił o ocenę przygotowywanego certyfikatu Sun Certified NetBeans IDE Specialist. Czy uważacie, że istnieje faktycznie potrzeba na Sun Certified Netbeans IDE Specialist? Co ono miałoby certyfikować?! Znajomość skrótów klawiszowych? Pozycji menu? A co w przypadku pracy z tłumaczonym środowiskiem? Możesz wyrazić swój głos w ankiecie Sun Certified NetBeans IDE Specialist. Miło zostałem zaskoczony zakresem egzaminu i uważam, że będzie doskonałym sprawdzianem poprawnego użycia NetBeans IDE jako środowiska pracy. Ciekawe, kiedy można oczekiwać odpowiedzi zespołu Eclipse. Fajne takie SCeNBIS oraz ECIS ;-)

Na koniec wiadomość z ostatniej chwili - pojawiła się ciekawa oferta na półce wydawnictwa Power Net - GWT w praktyce autorstwa Roberta Coopera oraz Charles'a Collinsa. Książka została przetłumaczona przez Marcina Leszczyńskiego, który znalazł swoje miejsce w podziękowaniach w wersji angielskiej (!) I ja przyłączam się do podziękowań za podjęcie trudu przetłumaczenia książki na polski. Wbrew panującemu obyczajowi na polskim rynku wydawniczym literatury informatycznej, tłumaczenie pojawiło się 2 miesiące po premierze angielskojęzycznej. Najwyraźniej można, jak się chce. Niedawno miałem okazję ponownie powalczyć z GWT i przymierzałem się do oryginalnej wersji książki, ale skoro jest dostępne polskie tłumaczenie, dlaczego nie zacząć lektury właśnie od niej? Jako rozgrzewkę można zabrać się za lekturę przykładowego rozdziału, który jest dostępny na stronie książki. Najwyraźniej Power Net zaczyna stanowić ciekawą alternatywę dla innych wydawnictw informatycznych z coraz to znaczącymi tłumaczeniami. Gratulacje!

15 czerwca 2008

Nowe JUGi w Opolu i Tarnowie oraz obsługa obrazków w GWT z ImageBundle

3 komentarzy
JAVArsovia 2008 za nami, a jej echo wciąż rozbrzmiewa. Powstają nowe grupy użytkowników technologii Java (JUG = Java User Group) i na scenie pojawiła się opolska grupa JUG - Opole JUG. Mówi się, że Opole to miasto .Net i PHP, więc dużo pracy przed opolskimi jugowiczami. Andrzej - lider Opole JUG - pewnie nie przypuszcza jak wiele pracy na niego czeka ;-) Podczas rozmowy z MaGiem* udało nam się namierzyć połączenia do Opola z Warszawy, które pozwalają na 6-godzinny pobyt w Opolu - wyjazd o 06:45-11:11 (InterCity), a powrót 18:26-22:45 (ponownie IC). Zatem Opole w zasięgu.

Działają już domeny jugowe na jug.pl, więc wystarczy tylko pomyśleć o JUGu w swoim mieście i wejść do Warszawa JUG, albo Lublin JUG, albo Łódź JUG, albo...i tak dalej. A skoro o nich i o nowych JUGach, muszę jeszcze wspomnieć o kolejnej grupie na polskiej scenie javowej - Tarnow JUG. Ta grupa również w zasięgu kolei i bezpośrednie połącznie EX - 6:05 (10:16 w Tarnowie), bądź EC - 9:05 (13:09), a powrót EC - 16:47 (20:49 w Warszawie), czy EX - 18:32 (22:50), więc można obrócić w jeden dzień ;-) Dziękujemy naszemu MaGowi od jug.pl za założenie domeny tarnow.jug.pl!

[*] Dla niewtajemniczonych: MaG to Marek G., czyli głównodowodzący jug.pl (nie zdradzę nazwiska, bo mu się jeszcze w głowie poprzewraca od sławy ;-))

Od kilku dni dokańczam moją aplikacyjkę Szachy pisaną w Google Web Toolkit (GWT) - wspominałem o niej w GWT 1.5 i Eclipse Ganymede z Cypal Studio for GWT (RC6). Dzisiaj po obsłudze ruchów, przyszła pora na upiększenie jej w postaci dodania ikon reprezentujących bierki. Mój kompletny brak znajomości efektów graficznych pozwolił mi jedynie na odszukanie odpowiednich ikonek w Sieci i dopasowanie ich do aplikacji, tj. bierki białe mają białe ikony, podczas gdy czarne czarne i nie jest to bynajmniej realizowane przez odpowiedni styl (CSS) i potencjalnie przeźroczyste ikony, a po prostu parę ikon - czarną i białą per bierka.

Każda bierka to klasa dziedzicząca po com.google.gwt.user.client.ui.ToggleButton, gdzie w konstruktorze podaję wskazanie na odpowiadającą ikonkę. Nie spodziewałem się, że to wymaganie zostanie tak szybko zrealizowane w GWT (!) Zajęło mi to bodajże minuty (i wierzę, że tyle zajmie przedstawienie tego cudeńka). Efektem jest przesłanie pojedyńczego zbiorczego obrazka, które jest sklejeniem owych ikonek reprezentujących bierki. Wszystko z pomocą GWTowego ImageBundle.

Rozpoczynamy od stworzenia klasy, w zasadzie interfejsu rozszerzającego com.google.gwt.user.client.ui.ImageBundle.
 package pl.jaceklaskowski.gwt.szachy.client;

import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.ImageBundle;

public interface SzachyImages extends ImageBundle {

AbstractImagePrototype hydra_bialy();

AbstractImagePrototype hydra_czarny();

}
Dla każdego pliku graficznego o nazwie pewna_nazwa.[png|gif|jpg] tworzymy odpowiadającą jej metodę o sygnaturze:
 AbstractImagePrototype pewna_nazwa();
Wczytanie pliku następuje bezpośrednio z pliku o nazwie jak nazwa metody z rozszerzeniem png, gif, jpg (w tej kolejności). Możliwe jest skorzystanie z adnotacji @Resource, która wskaże na właściwą nazwę pliku, jeśli nie odpowiada ona nazwie metody.

Ikonki należy umieścić w katalogu, gdzie znajduje się nasza klasa rozszerzająca ImageBundle, tj. dla mojego pl.jaceklaskowski.gwt.szachy.client.SzachyImages, będzie to katalog pl/jaceklaskowski/gwt/szachy/client. Możliwe jest również załadowanie ikonek z pliku jar po wskazaniu przez wspomnianą adnotację @Resource.w

W klasie/metodzie, gdzie chcemy skorzystać z ikonek pozostaje skonstruować egzemplarz SzachyImages z pomocą GWT.create()
 public static final SzachyImages images = (SzachyImages) GWT.create(SzachyImages.class);
i wywołanie metody createImage()
 Szachy.images.hydra_bialy().createImage()
skonstruuje egzemplarz reprezentujący naszą ikonkę.

Jako przykład można skonstruować przycisk typu com.google.gwt.user.client.ui.ToggleButton, gdzie w jednym z konstruktorów podajemy właśnie ikonkę. ToggleButton to bardzo ciekawa kontrolka, która umożliwia wyrysowanie przycisku, z którym właściwy styl CSS pozwoli na symulowanie przycisku w stanie przyciśnięty, normalny i po najechaniu myszką. Wystarczy skojarzyć odpowiednią klasę stylu CSS z przyciskiem za pomocą metody setStylePrimaryName("szachy-BierkaButton") i z poniższym stylem:
 .szachy-BierkaButton
{
background-color : #ffa;
color : #555;
border : 2px solid #666;

cursor : hand;
cursor : pointer;
font-size : 75%;

width : 51px;
text-align : center;
}
.szachy-BierkaButton-up-hovering
{
color : #009;
background-color : #ffd;
border-top : 2px solid #fff;
border-left : 2px solid #fff;
border-right : 2px solid #888;
border-bottom : 2px solid #888;
}
.szachy-BierkaButton-down, .szachy-BierkaButton-down-hovering
{
background-color : #ff0;
border-top : 2px solid #333;
border-left : 2px solid #333;
border-right : 2px solid #ccc;
border-bottom : 2px solid #ccc;
}
efekt wciśnięcia i wyłączenia mam zrealizowany "za darmo". Sam styl znalazłem na stronie poświęconej przykładom GWT - Roughian Examples. Podobnie jak ikony zgarnięte z Sieci. Chętnie skorzystałbym z pomocy osób ze smakiem graficznym i posługującymi się stylami CSS, które mogłyby uatrakcyjnić moją gierkę. W zamian mogę zaoferować publiczne podziękowania, a w przypadku firm umieszczenie logo firmy w Notatniku. Chętni?

Aplikacja nie jest tak wspaniała jak Sudoku Tomka z Apus Interactive i pomijając przepiękną szatę graficzną brakuje w mojej co najmniej funkcjonalności Przeciągnij-i-Upuść. Może w następnej wersji?!

Gotowa aplikacja (z wieloma TODO) jako projekt eclipsowy jest do pobrania jako gwt-szachy.zip. Jest w niej tyle miejsc oznaczonych jako TODO, że zapewne tylko wytrwali zdołają przebrnąć przez nie i dostrzec coś użytecznego. Komentarze mile widziane.

Pytanie konkursowe: Jaka klasa GWT odpowiedzialna jest za skonstruowanie zbiorczego obrazka? Nagrodą jest możliwość pogrania w magiczne szachy mojego autorstwa ;-) Miłej zabawy!

09 czerwca 2008

GWT 1.5 i Eclipse Ganymede z Cypal Studio for GWT (RC6)

9 komentarzy
Już nie pamiętam, kiedy to było, kiedy ostatni raz tworzyłem aplikację z Google Web Toolkit (GWT). Ostatni raz zapewne to było przy Tworzenie aplikacji Google Web Toolkit z Java Persistence API. Dodając do tego wydanie GWT 1.5 Release Candidate 1 ze wsparciem dla konstrukcji Java 5 i aż się chce wrócić do tematu. Dużo się ostatnio pisze o tym konkursie Ganymede Around the World Contest (ukłony dla Jacka P. i Michała M.), więc postanowiłem sprawdzić jak się sprawy mają w kontekście tworzenia aplikacji z GWT w środowisku Eclipse Ganymede. Nie ukrywam, że długi czas zajęło mi zdecydowanie się na środowisko rozwoju aplikacji GWT i nie jest to pierwszy raz, kiedy się z tym borykam. Jeśli Eclipse i GWT, to na scenę wchodzi projekt Cypal Studio for GWT, który jest zbiorem wtyczek eclipsowych wspierających tworzenie aplikacji GWT. Ostatnia wersja R6 wyszła...36 godzin temu! Jak to bywa w takich sytuacjach, dla niektórych to nie lada stres dotknąć jeszcze ciepłego produktu, a dla pozostałych gratka skosztowania nowych funkcjonalności. Nie długo trwało, kiedy przekonałem się, że dla moich podstawowych wymagań tworzenia aplikacji GWT Cypal Studio for GWT zdecydowanie wystarczy. I wszystko działa jak należy, chociaż nie obyło się bez niespodzianek.

Zestawienie środowiska do tworzenia aplikacji GWT z Eclipse Ganymede i Cypal Studio for GWT rozpoczynam od pobrania Cypal Studio i najnowszej wersji GWT. Eclipse Ganymede już mam, więc mam go z głowy. Instalacja Cypal Studio to umieszczenie go w wybranym katalogu, np. C:\apps\eclipse-plugins\cypal.studio.for.gwt\eclipse\plugins (paczka dystrybucyjna narzędzia to zestaw wtyczek - pliki jar), a następnie w katalogu links w Eclipse umieszczam plik cypal.studio.for.gwt.link o następującej treści:
 path=C:/apps/eclipse-plugins/cypal.studio.for.gwt
Więcej o tym mechaniźmie instalacji wtyczek do Eclipse w Zarządzanie wtyczkami w Eclipse - Extension Location.

Teraz wystarczy prześledzić krótką acz treściwą dokumentację Cypal Studio for GWT i po 5 minutach można cieszyć się z gotowego środowiska, albo nawet gotowej aplikacji opartej o GWT.

Rozpoczynam od konfiguracji wtyczki w Eclipse - Window > Preferences > Cypal Studio i ustawiam parametr GWT Home. Następnie Ctrl+N i wybieram Dynamic Web Project, gdzie w Configuration pojawi się Cypal Studio for GWT (acz jedynie jeśli wersja Dynamic Web Module version jest niższa niż 2.5). Bez względu na wybór Configuration (czy istnieje czy nie, np. przy wyborze wspomnianej wersji 2.5) istnieje możliwość wyboru własnej konfiguracji (Custom) przez przycisk Modify (obok pola Configuration), gdzie widoczny jest Cypal's GWT Facet.

Jedna uwaga związana z tworzeniem projektu, w którym moglibyśmy wybrać Apache Tomcat 6.0 jako środowisko uruchomieniowe. Próba uruchomienia tak zestawionego projektu z GWT Hosted Mode Application kończy się błędem o niedostepności klas bądź niezgodności sygnatur metod - generalnie niezła jazda i nie wiadomo, o co chodzi. Już byłem bliski zrezygnowania z Eclipse na rzecz IntelliJ IDEA, który również udostępnia wsparcie dla projektów GWT, kiedy w końcu udało mi się rozpracować temat, co sprowadziło się do usunięcia serwera Apache Tomcat jako domyślnego środowiska uruchomieniowego. Nie mam pojęcia, czy z innymi środowiskami również można doświadczyć tego "cudeńka".

I na koniec uwaga o usunięciu pliku gwt-user.jar z dokumentacji Cypal Studio for GWT, która nie dotyczy wersji GWT 1.5. Nie trzeba sobie zaprzątać głowy tym plikiem.

Po utworzeniu projektu, wystarczy Ctrl+N i Cypal Studio > GWT Module, podać dane modułu GWT i voila - aplikacja gotowa!

Jako przykład jakiejkolwiek funkcjonalności wyłącznie w celach demonstracyjnych można skorzystać z dokumentacji javadoc dla GWT, gdzie przy każdej kontrolce pojawia się krótki przykład, na jej wykorzystanie, np. dokumentacja com.google.gwt.user.client.ui.ToggleButton. Pozostaje jeszcze zajrzeć do katalogu samples, uruchomić przykłady, rozejrzeć się po kodzie i można zaczynać coś poważniejszego.

Uruchomienie aplikacji to Run > Run Configurations > GWT Hosted Mode Application i wybranie projektu GWT (pole Project) z odpowiednim modułem (pole Module). Tak proste, jak prostym powinno być.

Pytanie konkursowe: Jak nazywa się zestaw wtyczek eclipsowych do tworzenia aplikacji GWT?

p.s. Miałem o tym pisać już wcześniej, ale dopiero teraz się zebrałem. Podczas, kiedy ja żyłem JAVArsovią 2008, moja Agatka zdecydowała się trochę zrewolucjonizować nasze rodzinne życie i postanowiliśmy (my?!) kupić nowe mieszkanie na Ursynowie. Żonuś moja kochana trochę sceptycznie podchodzi do próby nagłaśniania tematu na moim blogu, ale może w ten sposób zrekompensuję rodzinie straty czasowe, kiedy to przesiaduję na blogu zamiast zajmować się nią i być może namierzę ciekawe mieszkanie. Interesuje nas mieszkanie 4-pokojowe (75-90m2) na Ursynowie (preferowany Natolin), najlepiej za...darmo - może znajdzie się jakaś bogata ciocia z Ameryki ;-) Jeśli macie wieści o takim lokum na Usynowie koniecznie dajcie znać. Fajnie byłoby się pochwalić mieszkaniem kupionym przez bloga. Dziękuję!

14 maja 2007

Tworzenie aplikacji Google Web Toolkit z Java Persistence API

0 komentarzy
Poznając Google Web Toolkit (GWT) coraz bardziej doskwierał mi brak środowiska, które umożliwiłoby mi tworzenie aplikacji z GWT wykorzystując przy tym JPA i inne technologie Java EE wraz z uruchomieniem całej aplikacji na serwerze aplikacyjnym Java EE. Niby wszystko było jasne, jak należy wszystko zestawić, ale nie mogłem się zebrać i w końcu to sprawdzić w praktyce. Przypadkiem natrafiłem na ciekawy artykuł o GWT i Apache Geronimo - Build an Ajax-enabled application using the Google Web Toolkit and Apache Geronimo, Part 1: Run compiled Google Web Toolkit applications on Geronimo. Nie mogłem wymarzyć sobie lepszego materiału do zgłębiania tajemnic GWT, co zmotywowało mnie do zajęcia się tematem bliżej. Zaraz zabrałem się za lekturę. Tak na marginesie muszę przyznać, że artykuł pojawił się w samą porę, bo nie tylko, że poznam GWT bliżej analizując przykład z artykułu, to jeszcze będę miał dobre przygotowanie na konferencję JavaTechConf 2007 w Krakowie pod koniec maja, gdzie właśnie prezentuję Apache Geronimo i jego wsparcie dla Java EE 5. Jeśli, więc pokażę technologie Przemysłowej 5-tki w połączeniu z GWT będzie to z pewnością prezentacja, na której po prostu warto być ;-) Mam wiele pomysłów na moją godzinną prezentację i mimo bardzo krótkiego czasu na jej poprowadzenie, do worka technologicznego na konferencję dorzucam również i GWT. Obym nie obiecał gruszek na wierzbie, bo mam już w nim Apache Geronimo, Apache Maven 2, TestNG, JPA i zapewne JSF 1.2 (w wykonaniu Apache MyFaces). Szkoda, że konferencja jest w tak odległym terminie, bo znając siebie, coś na pewno jeszcze dorzucę i zamiast godziny będę prezentował cały dzień.

Wracając do moich doświadczeń z GWT i JPA przez ostatnie kilka dni, było ich tyle, że postanowiłem je spisać i udostępnić jako kolejny artykuł - Tworzenie aplikacji Google Web Toolkit z Java Persistence API. Czytelnik znajdzie tam informacje potrzebne do zestawienia środowiska programistycznego do tworzenia aplikacji z GWT i JPA oraz Apache OpenJPA, Apache Derby, Dozer i Jetty wszystko zarządzane przez Apache Maven 2. W końcu udało mi się zapoznać z ostatnimi z tematów w GWT, m.in. serializacją, kontrolkami graficznymi GWT i obsługą zdarzeń. Zeszło mi się trochę z tym środowiskiem (bodajże 4 dni) i ciekawym bardzo komentarzy i uwag odnośnie zawartości merytorycznej artykułu. Coś wydaje mi się, że trochę dużo tego wyszło i mało kto dotrwa z lekturą do końca. Może powinienem rozważyć nagrodzenie najwytrwalszych?!

Dalsze możliwości rozwoju aplikacji to udoskonalenie wyglądu interfejsu użytkownika i podział projektu na oddzielne podmoduły (w sensie M2) - dla części GWT i JPA. Jeśli będą chętni do udostępnienia swoich zmian w tym kierunku z miłą chęcią opublikuję je w ramach artykułu.

A skąd pomysł na aplikację Rejestr Osób? W czasach otwierania teczek tego typu aplikacje przeżywają swój okres rozkwitu, więc i ja musiałem spróbować. Oczywiście jest to namiastka prawdziwej aplikacji, ale jej tworzenie w GWT sprawiło mi wiele przyjemności, może nawet tyle, że zastanawiam się, co miałoby mnie skłonić do napisania jej korzystając z JSF. Na prawdę zaczynam mieć coraz większe wątpliwości przed wyborem tej jedynej technologii przy następnych projektach - wydaje się, że GWT spełnia wszystkie wymagania stawiane współczesnym szkieletom programistycznym wliczając w to wręcz banalny sposób tworzenia zaawansowanych interfejsów użytkownika, udostępnienie funkcjonalności Ajax, możliwość wykonywania zdalnych usług i tym samym wykorzystania usług serwera Java EE, obsługę przycisku wstecz, itp. Będę musiał poświęcić kilka chwil na głośne rozważania na ten temat, bo teraz na gorąco, po pomyślnie zakończonej integracji GWT i JPA zdecydowanie byłobym za GWT. Dzięki Janek, Grzesiek i Marcin za pomysł aplikacji!

Na sam koniec wrócę jeszcze do mojego ostatniego wpisu o SCA - SCA - Service Component Architecture - zaczynam rozpoznanie czegoś całkowicie dla mnie nowego. Nie ukrywam, że liczyłem na pewnien odzew w tej kwestii, ale dyskusja jaka rozwinęła się przeszła moje najśmielsze oczekiwania. Bardzo dziękuję wszystkim za komentarze, które zamierzam wykorzystać studiując specyfikację SCA. Zanim na dobre zaangażowałem się w zestawianie środowiska z GWT i JPA, udało mi się zapoznać z kilkoma materiałami nt. SCA i jedną z cech, które niezwykle mi się podobały była możliwość podmiany modułu SCA dynamicznie podczas działania aplikacji. Wydaje mi się, że przykład prezentujący tę możliwość w połączeniu z GWT mógłby być niezwykle interesujący. Wyobraźmy sobie aplikację składającą się z kilku modułów SCA (wykorzystanych przez zdalne usługi GWT), która pracuje nieprzerwanie z możliwością dynamicznej podmiany modułu. W ogóle, tworzenie modułu SCA jest tak łudząco podobne do tworzenia usługi zdalnej w GWT, że trudno o nich czytać nie sądząc, że czyta się o jednym i tym samym. Dla dalszego zobrazowania roli SCA możnaby porównać ją z OSGi do budowania modularnych architektur. Jeśli OSGi pozwala na wyszukanie modułu i jego zarządzanie podczas działania aplikacji, to różnica między SCA polega na zasięgu takiej funkcjonalności - w OSGi wszystko dzieje się w ramach pojedyńczej wirtualnej maszyny podczas, gdy w SCA nie ma żadnych takich ograniczeń - miejsce uruchomienia nie implikuje dostępnych usług. Ciekawe tylko, czy faktycznie moje rozumienie roli SCA i OSGi jest słuszne? Dużo dobrych technologii powstaje i człowiek nic innego by nie robił, jak tylko się nimi bawił i rozpoznawał. A tu Panie Jacku coraz cieplej za oknem i komu chciałoby się przesiadywać przez komputerem?! Ech, byle do zimy! ;-)

08 maja 2007

GWT RPC raz jeszcze, RequestBuilder oraz Timer

0 komentarzy
Na razie idzie niezwykle gładko, zdecydowanie za dobrze. Tak kończyłem ostatni wpis o GWT i muszę przyznać, że z każdym dniem, kiedy zasiadam do GWT coraz bardziej się upewniam, że jest to właściwa technologia kliencka do dalszej ewaluacji. Chciałbym, aby z każdym szkieletem programistycznym (*) było tak przyjemnie.

(*) Nie przekonuje mnie użycie tłumaczenia szkielet programistyczny jako odpowiednik angielskiego framework. Z chęcią zmieniłbym je na coś bardziej dźwięcznego. Może pora na nowe słówko?! Tyle ich powstaje, a my ciągle szukamy tłumaczeń.

Sukcesem dnia dzisiejszego w kategorii GWT jest stworzenie namiastki aplikacji pod szumnie brzmiącym tytułem Kółko i krzyżyk. Mimo, że zgodnie z wypowiedzią Reinier Zwitserloot na forum GWT - How do I start ?:

run a continuous connection**: You can't create a 'live' connection between webapp and client. Let's say you want to write 2 player pacman in GWT, with the two players connected through your server: Can't really be done; every time a player hits a key, you have to submit a complete HTTP request, with at least 200 bytes worth of headers and the like in there.

sądziłem, że jednak się da i...się nie udało. To znaczy, udało się jedynie zasymulować grę z pewnymi ograniczeniami, które powodują, że po 3 dniach walki stwierdzam, że Reinier miał w 100% rację ;-)

Mimo nieodpowiedniego przykładu do zebrania doświadczeń w GWT poznałem wiele ciekawostek związanych z tym środowiskiem. Możliwość skorzystania z Apache Maven 2 do zarządzania tworzeniem aplikacji dodatkowo uprościło przedsięwzięcie i pozwoliło mi na skupienie się na detalach GWT, a nie wszystkiego wokół, poza GWT (jak to zazwyczaj bywa).

Zanim rozpiszę się na dobre wspomnę, że archiwum forum GWT jest nieocenionym źródłem informacji. W zasadzie są tam wszystkie pytania z odpowiedziami, które samemu zadawałem sobie próbując nagiąć się do myślenia w tonie GWT. Technologia jest na tyle młoda, jednakże wystarczająco stabilna do tworzenia części klienckiej aplikacji, że śmiało można powiedzieć, że niewiele jest dostępnych aplikacji, które możnaby potraktować jako wzorcowe. Pozostaje, więc skorzystanie z archiwum forum i przeszukiwanie Internetu (prawdopodobnie również z Google).

Dla przypomnienia, co wciąż jednak umykało mojej uwadze, jest fakt, że GWT to tworzenie aplikacji myśląc o niej w kanonach JFC/Swing, gdzie (zdalne) usługi wykonywane są na serwerze i traktowane jako źródło danych (i nic poza tym), a część kliencka będzie wykonywana w przeglądarce jako JavaScript. Ponownie, wspominany już, Reinier Zwitserloot trafił w dziesiątkę ze swoim opisem części serwerowej w przewrotnie nazwanym wątku JRuby on Grails?:

GWT turns your server into a dumb terminal for data.

, co odzwierciedla problemy, których doświadczałem próbując współdzielić dane między klientami. Początkowo wykorzystałem do tego servlet i dane umieszczane w zasięgu application. Przez długi czas nie mogłem się z tym pogodzić, że wymieniając dane między klientami korzystam z ServletContext. Zazwyczaj przechowuję w nim dane konfiguracyjne bądź statyczne dane, a dane dynamiczne umieszczam w zasięgu sesji (session scope) bądź najczęściej (do czego przekonało mnie JSF) zlecenia (request scope). Okazało się, że to, co należało obsłużyć za pomocą "czystego" GWT RPC ja "infekowałem" servletami, niepotrzebnie komplikując zadanie.

Dla zobrazowania tematu przedstawię sposób komunikacji aplikacji GWT (wykonywanej w przeglądarce jako JavaScript) z częścią serwerową reprezentowaną przez servlet (wykonywanej na serwerze w dowolnej technologii, m.in. Java EE):

RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, "/iloscGraczy");
try {
builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
Window.alert(exception.toString());
}

public void onResponseReceived(Request request, Response response) {
WitajSwiecieGWT.this.iloscGraczy = Integer.parseInt(response.getText());
}
});
} catch (RequestException e) {
Window.alert("Failed to send the request: " + e.getMessage());
}

i przez zdalną usługę w "czystym" GWT RPC:

AkcjaAsync remoteService = Akcja.App.getInstance();
remoteService.zarejestrujGracza(new AsyncCallback() {
public void onSuccess(Object result) {
label.setText("Zarejestrowano nowego gracza");
}

public void onFailure(Throwable caught) {
Window.alert(caught.toString());
}
});

Niby to samo, ale zmienia się podejście do zdalnej usługi, która jest źródłem danych. Niepotrzebnie wprowadzałem kolejną technologię - servlet - do zrealizowania funkcjonalności, którą ostatecznie obsłużyłem jako zdalną usługę w GWT RPC. Oczywiście w sytuacjach migracji z jednej technologii klienckiej (wliczam w to również JSF) do GWT taka sytuacja może się zdarzyć, ale przy tworzeniu aplikacji "od zera" wydaje się być niepotrzebne.

W zasadzie zrozumienie GWT RPC jest kluczem do stworzenia wyrafinowanej aplikacji w GWT. Zakładam jednocześnie, że samo poznanie kontrolek graficznych GWT - przyciski, okna, etc. - jest nieodzownym elementem poznawania GWT, ale przy znajomości JFC/Swing możemy ten krok pominąć (zakładając, że to już się po prostu zna). Podobieństwo między GWT a Swing jest tak ogromne, że możnaby postawić pomiędzy nimi znak równości.

Skoro GWT RPC jest tak istotne, to może istnieje sposób na skorzystanie z niego nie tracąc wiele czasu na zrozumienie istoty jego działania? Sądzę, że istnieje i już po samym zapoznaniu się z dokumentacją GWT - Remote Procedure Calls wszystko powinno stać się jasne. O GWT RPC pisałem również poprzednio - GWT RPC - mechanizm zdalnego wywoływania procedur w GWT w Notatniku, więc w zasadzie większość, jeśli nie cała, teoria za GWT RPC została już zaprezentowana. Najtrudniej jest przestawić się z myślenia technologiami klienckimi uruchamianymi na serwerze (wspierającymi tworzenie interfejsu użytkownika, np. JSF) na technologię kliencką uruchamianą w przeglądarce tworzoną w...Javie, czyli GWT. GWT RPC jest na tyle istotne, że warto poświęcić mu ponownie kilka chwil.

Procedura tworzenia zdalnej usługi w GWT RPC.

Krok 1. Utworzenie interfejsu usługi Gra

Hmmm, brzmi jak pierwszy krok podczas tworzenia komponentów EJB 3.0, czy w ogóle, dowolnej, modularnej aplikacji. To się może podobać każdemu (!)

package pl.jaceklaskowski.gwt.kolkoikrzyzyk.client;

import com.google.gwt.user.client.rpc.RemoteService;

public interface Gra extends RemoteService {
/**
* Zarejestruj gracza
*
* @return true jesli gracz zostal zarejestrowany, wpp false
*/
public boolean zarejestrujGracza();
}

Uwaga 1: Część kliencka musi być w pakiecie client będącymi podpakietem dowolnego pakietu.

Uwaga 2: Interfejs usługi musi rozszerzać interfejs com.google.gwt.user.client.rpc.RemoteService.

Krok 2. Utworzenie interfejsu asynchronicznego GraAsync

To niestety zaczyna pachnieć nieświeżo - jak programowanie z EJB 2.1 i wersjami poprzednimi. Chciałoby się skorzystać z adnotacji, które w aktualnej wersji GWT 1.3.3 są niemożliwe ze względu na ograniczenia wspieranej wersji języka Java - 1.4.2 i niżej. Przeżyliśmy EJB 2.1, przeżyjemy i GWT 1.3.3.

package pl.jaceklaskowski.gwt.kolkoikrzyzyk.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.AsyncCallback;

public interface GraAsync {
/**
* Zarejestruj gracza
*
* @return true jesli gracz zostal zarejestrowany, wpp false
*/
void zarejestrujGracza(AsyncCallback async);
}

Uwaga 1: Interfejs asynchroniczny usługi musi być nazwany zgodnie z regułą - nazwa interfejsu usługi + Async.

Uwaga 2: Każda metoda interfejsu asynchronicznego musi składać się z dodatkowego, dodawanego na końcu listy parametrów wejściowych parametru o typie AsyncCallback.

Krok 3. Utworzenie implementacji usługi GraImpl

package pl.jaceklaskowski.gwt.kolkoikrzyzyk.server;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import pl.jaceklaskowski.gwt.kolkoikrzyzyk.client.Gra;

import javax.servlet.http.HttpServletRequest;

public class GraImpl extends RemoteServiceServlet implements Gra {
public boolean zarejestrujGracza() {
HttpServletRequest request = getThreadLocalRequest();
// pracuj z obiektem request
// wszystkie inne metody HttpServlet sa rowniez dostepne - jestemy w koncu w servlecie
return true;
}
}

Uwaga 1: Implementacja usługi musi znajdować się w odpowiednim podpakiecie server.

Uwaga 2: Implementacja jest wykonywana na serwerze i dowolna technologia może zostać użyta do jej utworzenia.

Uwaga 3: Implementacja musi rozszerzać interfejs usługi a nie interfejs asynchroniczny.

Uwaga 4: Dostęp do obiektu request i response możliwy poprzez metody RemoteServiceServlet.getThreadLocalRequest oraz RemoteServiceServlet.getThreadLocalResponse, odpowiednio.

Krok 4. Rejestracja usługi - KolkoIKrzyzyk.gwt.xml

Kolejny krok, który mógłby być zrealizowany przez adnotacje. W końcu czasy plików konfiguracyjnych w XML są daleko za nami, nieprawdaż?

<module>

<inherits name='com.google.gwt.user.User'/>

<entry-point class='pl.jaceklaskowski.gwt.kolkoikrzyzyk.client.KolkoiKrzyzykGWT'/>

<servlet path="/gra" class="pl.jaceklaskowski.gwt.kolkoikrzyzyk.server.GraImpl"/>
</module>

Uwaga 1: Rejestrujemy usługę zdalną za pomocą elementu servlet, którego atrybut path określa ścieżkę mapowania servletu, a class klasę implementacji usługi.

Krok 5. Wywołanie usługi

GraAsync graUslugaZdalna = (GraAsync) GWT.create(Gra.class);
ServiceDefTarget endpoint = (ServiceDefTarget) graUslugaZdalna;
endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "gra");

AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
// result zawiera egzemplarz zwrócony podczas wywołania zdalnej metody
// tutaj wykonujemy operacje na interfejsie użytkownika
}

public void onFailure(Throwable caught) {
// bardzo przykładowa realizacja
Window.alert(caught.toString());
}
}

graUslugaZdalna.zarejestrujGracza(callback);

Uwaga 1: W GWT nie istnieje możliwość wykonania usługi synchronicznie, więc należy "przestawić się" na myślenie asynchroniczne, tj. dane mogą nadejść po pewnym czasie.

Uwaga 2: Adres servletu w linii 3. przykładu, gdzie wykonywana jest metoda setServiceEntryPoint odpowiada dokładnie ciągowi znaków wpisanemu w pliku konfiguracyjnym modułu w atrybucie path elementu servlet.

Uwaga 3: Warto skorzystać ze "wzorca", które podpowiada IntelliJ IDEA 6, tj. dodać poniższą klasę do interfejsu usługi

public static class App {
private static GraAsync ourInstance = null;

public static synchronized GraAsync getInstance() {
if (ourInstance == null) {
ourInstance = (GraAsync) GWT.create(Gra.class);
((ServiceDefTarget) ourInstance).setServiceEntryPoint(GWT.getModuleBaseURL() + "gra");
}
return ourInstance;
}

co sprowadzi powyższy krok do następującego:

GraAsync graUslugaZdalna = Gra.App.getInstance();

AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
// result zawiera egzemplarz zwrócony podczas wywołania zdalnej metody
// tutaj wykonujemy operacje na interfejsie użytkownika
}

public void onFailure(Throwable caught) {
// bardzo przykładowa realizacja
Window.alert(caught.toString());
}
}

graUslugaZdalna.zarejestrujGracza(callback);

Krok 6. Integracja usługi z aplikacją

Samo wywołanie usługi należy podpiąć do wykonania zdarzenia w interfejsie użytkownika pamiętając, że dane napływają z opóźnieniem, tj. asynchronicznie.

Jest wiele ciekawych rozwiązań w GWT, ale poza GWT RPC do stworzenia mojej gry skorzystałem również z mechanizmu Timer. Zacznijmy od przykładu.

Timer t = new Timer() {
public void run() {
label.setText("Sprawdzam, czy zasiedli wszyscy gracze");
if (moznaRozpoczacGre()) {
label.setText("Wyłączam sprawdzanie - wszyscy gotowi, można zaczynać, zatem rozpoczynam grę");
cancel();
}
}
};
t.scheduleRepeating(5000); // uruchamiaj co 5 sekund

gdzie metoda moznaRozpoczacGre prezentuje się następująco:

private boolean moznaRozpoczacGre() {
// polacz sie z serwerem i sprawdz ilosc graczy
// wykonaj zdalną usługę i zwróć ilość graczy
label.setText("Ilość graczy: " + iloscGraczy);
return iloscGraczy == 2;
}

W przykładzie stworzyłem egzemplarz typu Timer i dostarczyłem własną realizację metody run, która będzie wykonywana cyklicznie, zgodnie z parametrem metody schedule lub scheduleRepeating. Różnica między nimi to czas trwania zegara i moment jego wywołania - w pierwszej zegar uruchomi się raz za zadany czas, podczas, gdy w drugim przypadku będzie wykonywał się do momentu wywołania metody cancel co zadany okres czasu. Jest to najbardziej zadowalające mnie rozwiązanie do symulacji ciągłej interakcji między klientem (przeglądarka) a serwerem.

Znajomość GWT RPC, Timer oraz przyzwyczajenie się do asynchronicznej natury GWT to klucz do tworzenia aplikacji z GWT. Reszta to szczegóły, w których trudno doszukiwać się diabła ;-) Pora na rekonesans po JPA!

06 maja 2007

Tworzenie aplikacji z Google Web Toolkit i Apache Maven 2

0 komentarzy
Po przeszukaniu forów i list dyskusyjnych w końcu udało mi się doszukać wtyczki dla Apache Maven 2 (M2), która pozwala na zarządzanie projektem korzystającym z Google Web Toolkit (GWT) - maven-googlewebtoolkit2-plugin. Zdumiewający jest brak informacji na ten temat, również na polskich forach, co prawdopodobnie wynika z faktu, że większość skryptów zarządzających projektem oparta jest o bezpośrednie wykorzystanie Apache Ant (co zapewnia samo GWT podczas tworzenia projektu skryptem projectCreator) lub skorzystanie ze skryptów Ant niewprost poprzez wtyczkę M2 - maven-antrun-plugin.

W kolejnym artykule zatytułowanym Tworzenie aplikacji z Google Web Toolkit i Apache Maven 2 przedstawiłem wtyczkę maven-googlewebtoolkit2-plugin dedykowaną do zadań związanych z projektem korzystającym z GWT i zarządzanym przez M2.

Tym samym udało mi się zestawić środowisko do tworzenia aplikacji GWT pod kontrolą Apache Maven 2. Jest to kolejny krok w realizacji pomysłu zestawienia środowiska do tworzenia aplikacji GWT z Java EE przy pomocy Apache Maven 2. Kolejne artykuły o aplikacjach z GWT i usługami serwera aplikacji Java EE, z M2 w tle niebawem. Na razie idzie niezwykle gładko, zdecydowanie za dobrze.

02 maja 2007

GWT RPC - mechanizm zdalnego wywoływania procedur w GWT

0 komentarzy
Kontynuuję rozpoznawanie GWT i do mojej pierwszej aplikacji korzystającej z Java EE i GWT pozostaje poznać mechanizm spinający świat technologii klienckich - uruchamianych w przeglądarce - z serwerowymi. Na scenę wchodzi - GWT RPC. Z nim czuję, że jestem bliżej celu.

Jak wspomniałem wcześniej budowanie aplikacji GWT to programowanie w Javie stron będących mieszanką technologii klienckich (z punktu widzenia architektur wielowarstowych Java EE, w których klientem jest przeglądarka) - HTML, JavaScript oraz Ajax (przez co rozumiem - przynajmniej obecnie - zarządzanie obiektem XMLHttpRequest oraz modyfikacją DOM).

Największym wyzwaniem dla programistów Java pracującymi z technologiami serwerowymi wspierającymi dynamiczne konstruowanie interfejsu użytkownika - JSF, JSP, Servlety i in. - jest zrozumienie końcowego wyniku tworzenia aplikacji GWT - utworzenie strony HTML z "dodatkami" nie będącymi w żaden sposób związanymi z Javą (poza jej rolą jako język do ich utworzenia).

Dla uproszczenia zrozumienia roli GWT w tworzeniu aplikacji internetowej (klient = przeglądarka) wyróżnijmy etapy w jej życiu (tylko te, które są wartościowe w naszej dywagacji o GWT):
  1. Programowanie - etap, w którym programista pisze kod źródłowy w Javie.
  2. Kompilacja - etap, w którym kod źródłowy jest zmieniany na postać akceptowaną przez środowisko uruchomieniowe (bajtkod lub HTML).
  3. Uruchomienie - etap, w którym bajtkod produkuje treść wysyłaną do przeglądarki.
Co łączy tworzenie aplikacji w Java EE i GWT to fakt, że są one dedykowane dla środowiska uruchomieniowego, w którym główną rolę odgrywa język Java. Kod źródłowy tworzony jest w Javie i to jest jedyna część wspólna. Pozostałe etapy są już inne. I tutaj właśnie tkwi problem w moim wcześniejszym zrozumieniu GWT. W jego przypadku, postacią uruchomieniową kodu źródłowego jest HTML podczas, gdy w technologiach Java EE jest to bajtkod (bo tak kończy program napisany w Javie). Oczywiście w efekcie i GWT i Java EE "produkuje" HTML, ale różnica tkwi w momencie jego dostępności. W przypadku GWT, HTML pojawia się już w kroku 2., podczas gdy w Java EE jest materializowany dopiero w etapie 3. - uruchomieniowym.

Pamiętając o różnicach między GWT a Java EE, tworzenie aplikacji będących mieszanką ich obu nie powinno stanowić problemu. Jak HTML (będący postacią wynikową kompilacji aplikacji GWT) mógłby zostać wkomponowany w aplikację Java EE każdy wie. Możemy zmodyfikować HTML na stronę JSP, która z kolei korzysta z innych technologii, jednakże ostatecznie struktura wynikowa strony przesyłanej do przeglądarki musi spełniać wymagania GWT. Nie przekreśla to możliwości skorzystania z innych rozwiązań, np. JSF (choć na chwilę obecną nie wiem jak miałoby to wyglądać praktycznie, a jedynie teoretycznie).

Kiedy tworzymy aplikację GWT mamy do dyspozycji kilka elementów składowych podczas jej tworzenia (etap programowanie), które były już przedstawiane poprzednio, jednakże warto o nich wspomnieć ponownie:
  • entry-point - punkt dostępowy - klasa realizująca interfejs EntryPoint, która docelowo stanie się stroną HTML. Jest to część kliencka GWT. Może istnieć wiele punktów dostępowych.
  • servlet - klasa rozszerzająca klasę RemoteServiceServlet, która jest definicją servletu - części aplikacji wykonywanej po stronie serwera (w sensie GWT i Java EE) i wywoływanej przez mechanizm GWT RPC. Klasa, z której dziedziczy servlet jest jedynie klasą pochodną znanej z Java EE klasy javax.servlet.http.HttpServlet i obsługuje mechanizm serializacji.
Wspomniany mechanizm zdalnego wywoływania procedur GWT (ang. GWT Remote Procedure Call lub w skrócie GWT RPC) to sposób na zdalne wywoływanie części serwerowej aplikacji w GWT. Podobnie jak tradycyjna aplikacja desktopowa ma możliwość pozyskiwania danych z różnych zewnętrznych źródeł, tak i aplikacja GWT ma taką możliwość. Nie jest to do końca równoważne, gdyż aplikacja GWT działa w ramach przeglądarki i obowiązuje ją zasada łączenia się jedynie z serwerem, z którego pochodzi, jednakże sama koncepcja pozyskiwania danych jest analogiczna. Sposób działania aplikacji GWT jest łudząco podobny do działania tradycyjnej aplikacji desktopowej. Za pomocą GWT RPC mamy możliwość przekazywania obiektów do/z serwera poprzez HTTP - proces serializacji. W skrócie wykorzystanie mechanizmu RPC w aplikacji sprowadza się do definicji servletu w pliku konfiguracyjnym aplikacji, definicji oraz realizacji interfejsu i ostatecznie na wywołaniu usługi z aplikacji.

Pora na krótką demonstrację teorii w praktyce, co powinno znacząco uprościć zrozumienie tematu. Stworzę aplikację WitajSwiecieGWT.

Definiujemy zdalną usługę - część serwerową aplikacji - w deskryptorze modułu - WitajSwiecieGWT.gwt.xml za pomocą elementu servlet.

<module>
<inherits name='com.google.gwt.user.User'/>

<entry-point class='pl.jaceklaskowski.gwt.witajswiecie.client.WitajSwiecieGWT'/>

<servlet path="/uslugaZdalna" class="pl.jaceklaskowski.gwt.witajswiecie.server.UslugaZdalnaImpl"/>
</module>

W punkcie dostępowym aplikacji - element entry-point - wywołana zostaje usługa zdalna jako wynik wciśnięcia przycisku.

package pl.jaceklaskowski.gwt.witajswiecie.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class WitajSwiecieGWT implements EntryPoint {
public void onModuleLoad() {

final Button b = new Button("Przycisnij");
b.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
UslugaZdalnaAsync remoteService = UslugaZdalna.App.getInstance();
remoteService.wykonajZadanieNaSerwerze("Jacek", "Laskowski", new AsyncCallback() {
public void onSuccess(Object result) {
b.setText("Zakonczono poprawnie - wynik: " + result);
}

public void onFailure(Throwable caught) {
b.setText("Zakonczono niepoprawnie");
caught.printStackTrace();
}
});
}
});
RootPanel.get().add(b);
}
}

Klasa reprezentująca zdalną usługę - UsługaZdalna - to jedynie definicja interfejsu (w przykładzie mamy do dyspozycji pojedyńczą metodę wykonajZadanieNaSerwerze).

Dla usprawnienia programowania z GWT IntelliJ IDEA dostarcza metodę pomocniczą do tworzenia egzemplarzy UslugaZdalna.

package pl.jaceklaskowski.gwt.witajswiecie.client;

import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.core.client.GWT;

public interface UslugaZdalna extends RemoteService {

public String wykonajZadanieNaSerwerze(String imie, String nazwisko);

/**
* Metoda pomocnicza utworzona przez IntelliJ IDEA
* Use UslugaZdalna.App.getInstance() to access static instance of NowyRemoteServiceAsync
*/
public static class App {
private static UslugaZdalnaAsync ourInstance = null;

public static synchronized UslugaZdalnaAsync getInstance() {
if (ourInstance == null) {
ourInstance = (UslugaZdalnaAsync) GWT.create(UslugaZdalna.class);
((ServiceDefTarget) ourInstance).setServiceEntryPoint(GWT.getModuleBaseURL() + "uslugaZdalna");
}
return ourInstance;
}
}
}

Zgodnie z wymaganiem GWT, tworzony egzemplarz musi być rzutowany na typ UslugaZdalnaAsync.

package pl.jaceklaskowski.gwt.witajswiecie.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface UslugaZdalnaAsync {
void wykonajZadanieNaSerwerze(String imie, String nazwisko, AsyncCallback async);
}

Aż w końcu należałoby zapoznać się z implementacją interfejsów w postaci usługi zdalnej wykonywanej na serwerze - klasa UslugaZdalnaImpl. Tutaj jedynym ograniczeniem jest nasza wyobraźnia. Wszystko wywoływane w ramach klasy jest wykonywane na serwerze i nie podlega obsłudze GWT. To jest miejsce pozyskiwania danych, np. poprzez JPA.

package pl.jaceklaskowski.gwt.witajswiecie.server;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import pl.jaceklaskowski.gwt.witajswiecie.client.UslugaZdalna;

public class UslugaZdalnaImpl extends RemoteServiceServlet implements UslugaZdalna {
public String wykonajZadanieNaSerwerze(String imie, String nazwisko) {
System.out.println("UslugaZdalnaImpl.wykonajZadanieNaSerwerze");

// Tutaj możnaby oczekiwać bardziej wyrafinowanego przetwarzania
return imie + " " + nazwisko;
}
}

Ważne, aby pamiętać, że mimo ograniczenia na konstrukcje językowe Java w GWT w części klienckiej (które muszą odpowiadać tym w Java SE 1.4 i wcześniejszym), część serwerowa nie leży w gestii zainteresowania GWT (poza udostępnieniem mechanizmu RPC) i może być tworzona z dowolnymi mechanizmami i technologiami Java SE czy EE.

Tworzenie usług zdalnych w GWT przypomina tworzenie komponentów EJB w wersji 2.1 i poprzednich, gdzie należało zdefiniować coś na kształt interfejsu biznesowego, interfejs domowy i właściwą klasę komponentu wszystko spięte za pomocą deskryptora ejb-jar.xml. Brakuje usprawnień, do których zdążyłem sie już przyzwyczaić pracując z Java EE 5, m.in. tworzenie komponentów (czy usług) w tradycyjny sposób - interfejs oraz klasa realizująca z adnotacjami. Może nadchodząca wersja GWT 2.0 będzie sprytniejsza?!

Na zakończenie strona HTML uruchamiająca GWT.

<html>
<head>
<title>Aplikacja WitajSwiecieGWT</title>
<meta name='gwt:module' content='pl.jaceklaskowski.gwt.witajswiecie.WitajSwiecieGWT'>
<link rel=stylesheet href="WitajSwiecieGWT.css">
</head>
<body>
<script language="javascript" src="gwt.js"></script>
<iframe id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe>
<h1>Aplikacja WitajSwiecieGWT</h1>
</body>
</html>

Cały moduł gotowy do uruchomienia można pobrać jako WitajSwiecieGWT.zip. Wymagane jest jedynie zainstalowanie GWT (i posiadanie MS Windows do uruchomienia skryptów ;-)).

Na zakończenie, ważna uwaga w kontekście tworzenia aplikacji GWT w środowisku IntelliJ IDEA 6.0 - Enable "before launching" steps. Podczas uruchamiania projektu należy włączyć tę opcję, gdyż w przeciwnym przypadku część serwerowa nie zostanie zbudowana i uruchomienie mechanizmów GWT RPC zakończy się komunikatem błędu, który może prezentować się w ten sposób:


co wynika wyłącznie z niedostępności skompilowanej klasy na ścieżce klas.

Przy okazji rozwiązywania problemu (co okazało się być wyłącznie związane z samym posługiwaniem się IDEA) zauważyłem, że próbowałem korzystać z tradycyjnych metod tworząc aplikację GWT, np. caught.printStackTrace(), czy pomysłami w stylu instalacja Java SE 1.4 (co szczęśliwie zakończyło się przypomnieniem, że Java SE 5 ma możliwość kompilacji i uruchomienia z obniżoną wersją języka). Zauważam, że GWT wymaga pewnego oderwania się od dotychczasowych przyzwyczajeń i to, co było dobre w innych serwerowych technologiach tu może nie być najwłaściwszym podejściem. Ważne, aby podczas programowania z GWT korzystać z narzędzi, które są oferowane z GWT - GWT Development Shell z opcją śledzenia wykonywania programów (debug) czy podświetlenia linii z błędem, która zostanie uzupełniona o informacje pomocne w rozwiązywaniu aktualnego problemu (jak widać na powyższym zrzucie ekranu). Niestety nad rozwiązaniem tego (a przy okazji i zdobywaniem kolejnych informacji o GWT) spędziłem bodajże około 2 dni (!) Czasami warto zapomnieć o tym co się wie i zacząć wszystko od nowa, bo doświadczenie to często niepotrzebny bagaż (doświadczyłem tego na własnej skórze, kiedy uczyłem się, początkowo nieosiągalnej, sztuki żonglowania).

25 kwietnia 2007

Teoretyczne wprowadzenie do GWT

0 komentarzy
Zanim podejdę do przedstawienia Google Web Toolkit (GWT), uczynię swoją powinność jak napisano na stronach Google:

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 2.5 Attribution License.

Nie długo trwało zanim okazało się, że chciałem zacząć ten wpis tymi samymi słowami, jakimi rozpoczął swoje przedstawienie GWT Rafał Malinowski na swoim blogu. Natrafiłem na jego blog przeszukując materiały o GWT po polsku (nota bene, przypomniałem sobie, że o Rafale wspominał również Brzezi po moim pytaniu o wprowadzenie do GWT - Kilka pytań początkującego o GWT (było: Re: Spring + GWT) na grupie pl.comp.lang.java) .

Sercem GWT jest konwerter kodu w Javie (wersja 1.4.2 i poprzednie) na JavaScript.

I możnaby na tym poprzestać, gdyby nie fakt, że dla wielu (do niedawna również i dla mnie) JavaScript nie kojarzył się nierozerwalnie z technologią AJAX (mimo, że powinien, chociażby z samej nazwy AJAX - Asynchroniczny JavaScript i XML). I mimo, że GWT to konwerter Java do JavaScript to niekoniecznie musiało to implikować również zawarcie AJAXa. W przypadku GWT mamy do dyspozycji możliwość tworzenia kodu w Javie, który jest bardzo zbliżony do tworzenia aplikacji z wykorzystaniem biblioteki JFC/Swing, w tym i obsługą zdarzeń, która ostatecznie kończy swój "żywot rozwojowy" jako aplikacja napisana w JavaScript, właśnie ze wsparciem dla AJAX.

Dla mnie osobiście, ważne jest, że GWT pozwala zapomnieć o (X)HTML, DHTML, JavaScript i ostatecznie AJAX (czyli interakcją z XMLHttpRequest wraz z modyfikacją DOM) oraz o różnicach w realizacji ich w przeglądarkach - IE, FF, Safari i Opera (zobacz: Cross-browser Support).

Istotnym elementem jest również wykorzystanie dostępnych elementów HTML do konstrukcji aplikacji korzystającej z GWT. Strona HTML z kodem tworzonym przy pomocy GWT (ang. HTML host page) jest możliwa do uruchomienia bez specjalnych wtyczek/rozszerzeń po stronie klienta-przeglądarki, co czyni tworzenie aplikacji internetowej z wykorzystaniem GWT bezinwazyjnym i nie nakłada (prawie) żadnych wymagań, będąc wyłącznie "czystym" JavaScriptem.

Strona GWT (tymczasowa nazwa dla HTML host page) jest tworzona jako wynik transformacji kodu napisanego w Javie, gdzie mamy możliwość skorzystania z silnego typowania języka i skorzystać z dostępnych zintegrowanych środowisk programistycznych (ang. IDE - integrated development environment) wspierających tworzenie aplikacji w tym języku, do strony całkowicie pozbawionej elementów pisanych w Javie, a jedynie JavaScript.

Jak z każdą aplikacją HTML tak i w przypadku aplikacji GWT (która docelowo staje się aplikacją HTML z JavaScript) do ustawiania wizualizacji (stylu) kontrolek (ang. GWT widget) służy kolejny ustandaryzowany język - język styli CSS (zobacz: Style Sheets). Jest to kolejne wykorzystanie istniejącej technologii/języka, co czyni rozpoznanie i korzystanie z GWT prostszym.

Mamy dwa tryby pracy z GWT - tryb hosted (rozwojowy), w którym następuje uruchomienie testowe aplikacji przy pomocy specjalnie stworzonej dla GWT przeglądarki bez translacji kodu w Javie do kodu wynikowego w JavaScript oraz web (produkcyjny), gdzie klientem jest typowa przeglądarka (zobacz: Debugging in Hosted Mode). Większość naszego czasu nad rozwojem aplikacji GWT będziemy spędzali na pracy w trybie rozwojowym. Pozwala to na pełne wykorzystanie dostępnych narzędzi do tworzenia aplikacji Java. Podczas pracy w trybie rozwojowym, w przeglądarce GWT możemy skorzystać z przycisku Compile/Browse do wygenerowania odpowiedniego kodu w JavaScript bądź skorzystać ze skryptów tworzonych automatycznie podczas zestawiania projektu GWT.

Ważna podkreślenia jest ciągle powtarzana, ale jednocześnie trudna do zapamiętania, fundamentalna cecha GWT, która sprawia, że w trybie produkcyjnym aplikacja GWT to wyłącznie JavaScript i inne technologie klienckie (interpretowane przez przeglądarkę, jak CSS, DHTML, XML). Tym samym nie wymaga się żadnej wtyczki/rozszerzenia ani wirtualnej maszyny Java (JVM) do produkcyjnego uruchomienia aplikacji GWT.

GWT dostarcza skrypty tworzące projekt oraz skrypty uruchomieniowe dla narzędzi pomocniczych (zobacz: Command-line Tools):
  • projectCreator - służy do utworzenia struktury katalogowej projektu i opcjonalnych plików projektowych dla Ant i Eclipse.
  • applicationCreator - służy do utworzenia aplikacji, skryptów do uruchomienia jej w trybie rozwojowym oraz transformacji (kompilacji) do JavaScript.
  • junitCreator - służy do utworzenia testów jednostkowych JUnit i skryptów do uruchomienia w trybie rozwojowym i produkcyjnym (w zwykłej przeglądarce).
  • i18nCreator - usprawnia proces umiędzynarodowienia aplikacji (ang. i18n - internationalization), czyli dostosowywania aplikacji do wymagań specyficznych dla danego kraju.
W GWT mówi się o dwóch stronach aplikacji - stronie klienckiej (przeglądarka) oraz serwerowej (Client-side Code oraz Server-side Code). Mimo, że cała aplikacja GWT staje się ostatecznie wyłącznie zbiorem plików z JavaScript, HTML i CSS, i nie byłoby mowy o stronie innej niż przeglądarka, to w przypadku korzystania z AJAX (czyli GWT RCP - remote procedure call (RPC)) odbiorcą jest część aplikacji (w zasadzie powinienem napisać rozwiązania, aby nie wprowadzać zamieszania z ponownym użyciem słowa aplikacja w innym kontekście), która uruchamiania jest po stronie serwera i która w żaden sposób nie jest modyfikowana przez GWT. Strona serwerowa nie leży w gestii zainteresowania GWT poza udostępnieniem możliwości jej wywołania za pomocą RCP.

Przechodząc do samego tworzenia aplikacji GWT, wyróżnić należy kilka z nim związanych pojęć (patrz: Modules):
  • Moduł - module - pliki XML z konfiguracją GWT odpowiadającą jednostkom funkcjonalnym aplikacji. W module znajdują się wszystkie ustawienia konfiguracyjne niezbędne do poprawnego uruchomienia aplikacji GWT. Zalecane jest, aby moduł (plik konfiguracyjny XML aplikacji GWT) występował w pakiecie głównym projektu. Moduły definiują klasy, które podlegają translacji do JavaScript.
  • Punkt dostępowy - entry-point - jest klasą rozszerzającą interfejs EntryPoint. Podczas uruchamiania modułu, każdy punkt dostępowy jest tworzony i wywoływana jest jego metoda EntryPoint.onModuleLoad().
GWT korzysta z pojęcia pakietów do tworzenia struktury projektu. Domyślny układ pakietów GWT rozdziela części klienckie (uruchamiane w przeglądarce) od serwerowych (uruchamianych na serwerze i nie modyfikowanych przez GWT). Patrz: Project Structure.

Po lekturze wprowadzającej do GWT (Developer Guide Fundamentals) nie pozostaje nic innego jak próba zbudowania projektu GWT z użyciem M2 (najprawdopodobniej za pomocą wtyczki maven-antrun-plugin) oraz skorzystania z części serwerowej opartej o technologie przemysłowej 5-tki (Java EE 5). Już nie mogę się doczekać.

24 kwietnia 2007

Pierwsze potyczki z GWT - szczypta technologii klienckiej dla urozmaicenia

1 komentarzy
Może nie pierwsze teoretycznie, ale z pewnością pierwsze praktycznie. Po prezentacji Michała Margiela - Miesięczna ewaluacja Google Web Toolkit (GWT) - i co, fajne? - na spotkaniu Warszawa JUG myśl o konieczności samodzielnego popróbowania się z Google Web Toolkit (GWT) nie dawała mi spokoju. Jakby tego było mało natrafiłem na relację Java i AJAX w praktyce: system internetowy w GWT autorstwa lukjel. Tego było już za wiele. Czułem, że coś mnie omija. Wszyscy się dobrze bawią z GWT, a ja stoję gdzieś z boku, w kącie i zastanawiam się skąd dochodzą odgłosy zabawy ;-)

Pobrałem GWT 1.3.3 dla Windows i przeczytałem Google Web Toolkit Getting Started Guide. Niewiele tego jak na początek, pomyślałem, ale może właśnie w tym tkwi siła GWT - prostota tworzenia aplikacji, której nie ma co opisywać, bo się sama tłumaczy. I długo nie musiałem czekać, żeby się o tym przekonać (w międzyczasie lektura specyfikacji JPA zajęła mnie na trochę, więc faktycznie trwało to dłużej zanim zabrałem się za GWT).

Właśnie wczoraj, w z korespondencji otrzymałem propozycję skorzystania z akademickiej licencji dla IntelliJ IDEA. Jako członek zespołów projektów otwartych miałem już dla nich licencję, więc pomyślałem, że nawet jeśli nie skorzystam z propozycji, to może warto sprawdzić jak się sprawuje IDEA i w międzyczasie uaktualnić ją. Po aktualizacji do wersji 6.0.5 moim oczom ukazał się panel powitalny z...GWT Studio. Przypomniałem sobie o tych wszystkich intrygujących artykułach i wypowiedziach związanych z GWT i tym razem nie mogłem już odkładać tematu na później. Musiałem coś własnego zobaczyć działającego, co byłoby napisane w GWT. Zabrałem się za jego rozpracowywanie.

Nie będę reklamował komercyjnego produktu jakim jest IntelliJ IDEA, ale warto podkreślić, że cena za możliwości narzędzia jest niewspółmierna (na korzyść IDEA). Wsparcie dla GWT jest dokładnie, jakiego mógłby oczekiwać taki nowicjusz GWT jakim ja jestem. Zresztą można samemu się przekonać, obejrzawszy film - Google Web Toolkit as 1-2-3. Więcej o GWT i IDEA. Biorąc pod uwagę wsparcie dla EJB 3.0 i JPA i kilku innych technologii zdaje się, że do zbioru na codzień wykorzystywanych IDE dodam kolejne - IntelliJ IDEA. Ufff, to już 3, nie wspominając o komercyjnych ofertach od IBM (IBM Rational Application Developer 7) czy BEA (BEA Workshop Studio 3.3). I jeszcze jest Exadel Studio Pro. Każde ma coś unikatowego. Szczęśliwie ostatnie produkty to jedynie rozszerzenia dla Eclipse IDE, więc można pracować ze wszystkimi, jakby były pojedyńczym produktem.

Wracam do tematu głównego - GWT. Pierwszy strzał to oczywiście uruchomienie przykładów rozprowadzanych z GWT, które znaleźć można w katalogu samples. Dalej prześledzenie filmu o IDEA i ostatecznie pomysł, aby spróbować skorzystać z GWT i jego narzędzi do generowania struktury projektu - projectCreator oraz applicationCreator, które pozwalają na przygotowanie projektu dla Eclipse IDE. Możliwość tworzenia projektów korzystających z GWT w Eclipse IDE zmniejsza wymagania wstępne (niweluje potrzebę korzystania z IntelliJ IDEA chociażby za cenę większego wysiłku i prawdopodobnie czasu).

c:\projs> mkdir witajswieciegwt

c:\projs> cd witajswieciegwt

C:\projs\witajswieciegwt> c:\apps\gwt\projectCreator -eclipse witajswieciegwt
Created directory C:\projs\witajswieciegwt\src
Created directory C:\projs\witajswieciegwt\test
Created file C:\projs\witajswieciegwt\.project
Created file C:\projs\witajswieciegwt\.classpath

C:\projs\witajswieciegwt> c:\apps\gwt\applicationCreator -eclipse witajswieciegwt pl.jaceklaskowski.klient.WitajSwiecieGWT
'pl.jaceklaskowski.klient.WitajSwiecieGWT': Please use 'client' as the final package, as in 'com.example.foo.client.MyApp'.
It isn't technically necessary, but this tool enforces the best practice.
Google Web Toolkit 1.3.3
ApplicationCreator [-eclipse projectName] [-out dir] [-overwrite] [-ignore] className

where
-eclipse Creates a debug launch config for the named eclipse project
-out The directory to write output files into (defaults to current)
-overwrite Overwrite any existing files
-ignore Ignore any existing files; do not overwrite
and
className The fully-qualified name of the application class to create

Co za gość?! Nie mogę nazwać mojego pakietu jakbym chciał. Uroki (wymuszonej) konfiguracji domyślnej?! Oby się opłacało.

C:\projs\witajswieciegwt> c:\apps\gwt\applicationCreator -eclipse witajswieciegwt pl.jaceklaskowski.client.WitajSwiecieGWT
Created directory C:\projs\witajswieciegwt\src\pl\jaceklaskowski
Created directory C:\projs\witajswieciegwt\src\pl\jaceklaskowski\client
Created directory C:\projs\witajswieciegwt\src\pl\jaceklaskowski\public
Created file C:\projs\witajswieciegwt\src\pl\jaceklaskowski\WitajSwiecieGWT.gwt.xml
Created file C:\projs\witajswieciegwt\src\pl\jaceklaskowski\public\WitajSwiecieGWT.html
Created file C:\projs\witajswieciegwt\src\pl\jaceklaskowski\client\WitajSwiecieGWT.java
Created file C:\projs\witajswieciegwt\WitajSwiecieGWT.launch
Created file C:\projs\witajswieciegwt\WitajSwiecieGWT-shell.cmd
Created file C:\projs\witajswieciegwt\WitajSwiecieGWT-compile.cmd

Otwieram Eclipse IDE 3.3m6 i importuję projekt WitajSwiecieGWT. Po zaimportowaniu w menu Run -> Run As pojawia się nowa konfiguracja uruchomieniowa - WitajSwiecieGWT. Próba uruchomienia i okazuje się, że projekt...działa!


Wciskając przycisk Click me wywołujemy następujący kod

button.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
if (label.getText().equals(""))
label.setText("Hello World!");
else
label.setText("");
}
});

, który wyświetla Hello World!.

Na zakończenie sprawdzę, jak bardzo dynamiczne jest tworzenie aplikacji z GWT i Eclipse.

Zmieniłem tekst Hello World! na Witaj Świecie GWT! i odświeżyłem przeglądarkę GWT. I znowu poprawnie. Odświeżyło się. Niestety, coś nie tak pojawiło się z kodowaniem polskich znaków. Zmieniłem Preferences->General->Workspace->Text file encoding w Eclipse na UTF-8, co wymagało również poprawienia 'Ś' w klasie i po ponownym odświeżeniu pojawiła się i polska litera.

Na koniec wykonałem skrypt WitajSwiecieGWT-compile.cmd. W wyniku uruchomienia pojawiły się dwa katalogi tomcat oraz www. Otworzyłem plik www/pl.jaceklaskowski.WitajSwiecieGWT/WitajSwiecieGWT.html w przeglądarce (Firefox 2.0.0.3 na Windows) i...znowu działa.

Szybko, sprawnie i przyjemnie. Zdecydowanie za lekko idzie ;-) Już mi się marzy budowanie za pomocą M2 z Java EE w tle.