GAE/J - Hello World (2)

piątek, 17 kwietnia 2009

Oto mały opis moich eksperymentów z Google App Engine for Java

Część pierwsza, Część druga

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:

HelloWorld.java

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.

Something.java

@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.

Install.java

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..

Show.java

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.

Something.java

    public @JSON String getName() { return name; }
    public @JSON String getValue() { return value; }

Następnie wystarczy nieznaczna modyfikacja kontrolera..

Show.java

    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: , , ,

Red 12:29

Komentarze

Prześlij komentarz

Archiwum

Subskrybuj

RSS / Atom