GAE/J - Hello World (2)
piątek, 17 kwietnia 2009
Oto mały opis moich eksperymentów z Google App Engine for Java
Od chwili gdy zacząłem pisać ten tekst minęło kilka dni. W tym czasie w sieci pojawiło się sporo interesujących artykułów na temat GAE/J. Najciekawsze z nich dotyczą wykorzystania dynamicznych języków.
Aktualizowany na gorąco spis można znaleźć w dokumencie
Frameworks and libraries supported by Google App Engine Java
.
Najciekawszy projekt jaki znalazłem, to konsola pozwalająca na eksperymenty
z prostymi skryptami w kilku językach jednocześnie.
Wzmiankę zamieściłem w kategorii Znalezione
jakiś czas temu. Polecam.
Many languages, and in the runtime bind them
.
Demonstracja, wraz z kodem źródłowym znajduje się pod adresem
lotrepls.appspot.com.
Na tle wspomnianych przykładów, to co piszę wypada dość blado, językiem Java zajmowałem się nieco poważniej w 1998 roku i od tego czasu jedynie sporadycznie poświęcałem mu uwagę. Tak więc, mając nadzieję że to co piszę, przyda się przynajmniej początkującym, opisuję moje dalsze zmagania z GAE/J.
Tym razem mam zamiar opisać coś ciekawszego, przedstawiony tu prosty programik od początku do końca działa w oparciu o mechanizmy serializacji.
Makefile (build.xml)
Poprzedni opis zakończyłem na uruchomieniu przykładowej aplikacji, nie wnikając w szczegóły jej funkcjonowania.
Jeśli ktoś chciał by podany przykład rozbudować, szybko odkrył by poważne ograniczenia, jakie ma domyślny plik build.xml. Chcąc rozwiązać ten problem, stworzyłem własny szablon aplikacji, obsługujący domyślnie wymagane mechanizmy i łatwiejszy do dalszej rozbudowy.
Szablon ten jest dostępny na stronie: code.google.com/p/szsz/downloads/.
Główna zmiana dotyczy pliku build.xml, który teraz jest na tyle uniwersalny, by nie wymagać dalszych modyfikacji.
Wszystkie pliki xml w szablonie zawierają odpowiednie przykłady i odnośniki do dokumentacji. Zachęcam do przetestowania i liczę na ewentualne uwagi.
HelloWorld
App Engine wykorzystuje Servlet API. Rozwiązanie opracowane przez firmę Sun Microsystems w 1998 roku, gdy stało się jasne że wykorzystanie, popularnego wówczas, interfejsu CGI w języku Java nie wchodzi w grę.
Osoby znające lekkie frameworki MVC poczują się swojsko gdy tylko zobaczą pierwszy przykład kodu:
public class HelloWorld extends HttpServlet { public void doGet( HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.getWriter().println("Hello, world"); } }
To nie wymaga żadnych dodatkowych komentarzy.
Serializacja
GAE/J ma jedną cechę za którą wielu programistów Java pewnie znienawidzi
to środowisko. Część rdzennego API, do którego dotychczas stosowano
założenie typu zawsze działa
, jest niedostępna.
Dotyczy to głównie obsługi wątków, dostępu do relacyjnych
baz danych i możliwości modyfikowania systemu plików.
Jedynym mechanizmem służącym do przechowywania danych jest Datastore, do którego dostęp odbywa się poprzez API do serializacji danych (JDO, JPA). W ten sposób serializacja staje się podstawą działania każdej aplikacji w tym środowisku.
Tym razem przykład będzie się składał z kilku części i będzie w pełni oparty o model MVC.
Do dzieła..
Model
Czyli, klasa obiektów służących do manipulowania danymi.
Niech będzie to prosta tabela słownikowa, typu nazwa
=wartość
.
Oczywiście aby aplikacja miała sens, potrzebuje jeszcze paru metod, nadających obiektowi życie i adnotacji, pozwalających na zapisywanie stanu.
@PersistanceCapable class Something { @PrimaryKey private String name; @Persistent private String value; Test(String k, String v) { name = k; value = v; } String doSomething() { return name + ", " + value; } }
To wszystko. Kluczem do serializacji są adnotacje @PersistanceCapable, @PrimaryKey i @Persistent.
Adnotacje, są pewnego rodzaju specjalnymi komentarzami/dyrektywami, rozszerzającymi opis klasy na użytek funkcji korzystających z Reflection API
Przed wydaniem Java 1.5, serializacja w tym języku przypominała czarną magię i wymagała odrębnego pliku xml z opisem pól i ich mapowania na bazę danych. Na szczęście te ponure czasy już minęły. Teraz definicja klasy zawiera wszystkie informacje potrzebne do automatycznego zbudowania odpowiedniego kodu.
Kontroler (inicjalizacja)
By zobaczyć w działaniu nasz prosty model, będziemy potrzebowali równie prostego kontrolera.
Na początek będzie potrzebny prosty servlet tworzący trwałe
obiekty.
Ponieważ nie było by wskazane aby był dostępny dla postronnych osób,
do uruchomienia będą wymagane odpowiednie uprawnienia, które ustawiamy
modyfikując plik web.xml.
Akcja jest wywoływana metodą POST, potrzebny będzie odpowiedni formularz: install/index.html.
class Install extends HttpServlet { public void doPost( HttpServletRequest req, HttpServletResponse resp) throws IOException { PersistenceManager pm = PMF.manager(); Something hello = new Something("Hello", "world"); pm.makePersistent(hello); /* ZAPIS */ pm.close(); resp.getWriter().println("DONE"); } }
Uwaga. Zaprezentowane tu zabezpieczenie jest dość mizerne. Należało by przynajmniej weryfikować czy akcja została wywołana przez właściwy formularz - bez tego, administratora tak napisanej aplikacji może spotkać przykra niespodzianka.
Dodatkowo do działania potrzebny jest singleton PMF.java. To rozwiązanie jest zalecane w dokumentacji GAE/J ze względów wydajnościowych, a przy okazji zaoszczędza trochę pisaniny.
Kontroler (podgląd)
Przydało by się zobaczyć efekty działania instalatora
.
Można by oczywiście skorzystać z opcji Data Viewer
w konsoli sterującej.
(Po uprzednim załadowaniu aplikacji na serwer produkcyjny), ale..
Tradycyjnie w tym miejscu tworzy się szablon JSP, który nieco przypomina skrzyżowani ASP i PHP. Wydawało by się że zapewnia to wystarczającą separację pomiędzy implementacją a wizualizacją. W wielu przypadkach rzeczywiście tak jest, ale można posunąć się dalej i całkowicie usunąć warstwę prezentacyjną z aplikacji działającej na serwerze.
Na początek, niezbędne minimum kodu..
class Show extends HttpServlet { public void doGet( HttpServletRequest req, HttpServletResponse resp) throws IOException { PersistenceManager pm = PMF.manager(); /* ODCZYT */ Something hello = pm.getObjectById( Something.class, "Hello" ); resp.getWriter().println( hello.doSomething() ); } }
.. oraz odpowiednie modyfikacje web.xml
W ten sposób udało się stworzyć program, który przy całej swojej złożoności,
angażując najnowocześniejsze techniki, produkuje wynik identyczny z pojedynczym poleceniem print
.
Kontroler (json)
Do operowania na danych została wykorzystana serializacja, a co by było gdyby tą samą technikę wykorzystać do przesłania danych na przeglądarkę?
Gdy zacząłem moje eksperymenty, sprawdziłem kilka rozwiązań.
Między innymi bilioteki z json.org, które okazały się być zbyt nisko poziomowe
i popularną bibliotekę xstream
, która wydawała się idealnym
rozwiązaniem, ale niestety zaczęła sprawiać problemy po instalacji
na serwerach GAE/J - to się wkrótce może zmienić, więc jeśli ktoś
woli używać xml, to polecam tą bibliotekę.
Wreszcie znalazłem rozwiązanie, które pasuje idealnie:
flexjson.
Flexjson
, używa wyłącznie formatu JSON. Domyślnie biblioteka serializuje publiczne pola.
Jednak podobnie jak w JDO można za pomocą adnotacji określić, które
właściwości mają być obsługiwane przez bibliotekę.
Największą zaletą biblioteki jest to że potrafi używać
getterów i setterów, co czyni ją niezwykle elastycznym
narzędziem. Dzięki temu nie trzeba traktować trwałych obiektów
jak sztywnych struktur danych.
Bibliotekę instalujemy kopiując plik flexjson.jar do katalogu lib. W szablonie, który tu zaprezentowałem ta biblioteka już jest zainstalowana.
Zacznijmy od określenia w modelu, które pola mają być wysyłane do odbiorcy.
public @JSON String getName() { return name; } public @JSON String getValue() { return value; }
Następnie wystarczy nieznaczna modyfikacja kontrolera..
Something hello = pm.getObjectById(Something.class, "Hello"); JSONSerializer ser = new JSONSerializer().exclude("class"); resp.getWriter().println( ser.prettyPrint(hello) );
To wszystko.
Widok
Pozostało już tylko wyświetlić przesłane dane na przeglądarce. Można to na przykład zrobić w taki mało elegancki sposób: index.html.
$(function() { $.getJSON("/show", function(data) { $("body").html( data.name + ", " + data.value ); }); });
Bardziej eleganckie rozwiązanie, wymagało by zastosowania odpowiednich szablonów. W końcu po stronie przeglądarki także warto oddzielić kod od warstwy prezentacyjnej :) ..ale to już kiedy indziej.
Podsumowanie
Pisanie przykładów takich jak ten, przypomina chwilami
budowę kanału panamskiego po to aby później puścić nim papierową łódkę.
Chwilami w gąszczu skomplikowanych konstrukcji,
można zagubić sens tego wszystkiego, myslę jednak że API do serializacji
jest na tyle proste że nie sprawia problemów ze zrozumieniem.. Natomiast
nowe podejście jest znacznie wygodniejsze od mozolnego łączenia się
z bazami danych, czytania licznych plików i składania informacji kawałek
po kawałku..
Gdyby kotoś chciał eksperymentować z przedstawionym tu kodem,
to jest on dostępny do pobrania za pomocą svn
svn co -r11 http://szsz.googlecode.com/svn/trunk/gae-java/hello hello-szsz
Miłej zabawy
Etykiety: gae, json, programowanie, R
Red 12:29
Prześlij komentarz