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):
- Programowanie - etap, w którym programista pisze kod źródłowy w Javie.
- Kompilacja - etap, w którym kod źródłowy jest zmieniany na postać akceptowaną przez środowisko uruchomieniowe (bajtkod lub HTML).
- Uruchomienie - etap, w którym bajtkod produkuje treść wysyłaną do przeglądarki.
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.
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).
Brak komentarzy:
Prześlij komentarz