[WikiDyd] [TitleIndex] [WordIndex

WarstwaPrezentacji

Czym jest GWT

Google Web Toolkit (GWT) jest „opensorsowym" framework`em który pozwala na tworzenie aplikacji webowych bazujących na AJAXie, za pomocą języka Java. Za pomocą kompilatora, kod Javy zostaje zamieniony na JavaScript i HTML.

Skąd wziąć biblioteki GWT?

Można je ściągnąć ze strony : http://google-web-toolkit.googlecode.com/files/gwt-windows-1.3.3.zip

Co nam będzie potrzebne do pracy z GWT?

Tworzenie projektu

Po rozpakowaniu pliku gwt-mac-1.3.3.zip możemy od razu przystąpić do pracy. Możemy stworzyć projekt zwykły GWT lub też zmusić kompilator do stworzenia projektu GWT jako projektu Eclipse.

Tworzenie projektów odbywa się z linii komend. Załóżmy że chcemy stworzyć projekt o nazwie ' MyApplication'.Do tego celu musimy wykorzystać narzędzie zwane applicationCreator, który automatycznie wytworzy wszystkie niezbędne pliki do startu projektu GWT. Ważne jest aby nasz projekt posiadał podpakiet zwany client. A więc polecenie stworzenia nowego projektu będzie wyglądało tak:

Aby uruchomić nasz projekt należy uruchomić skrypt MyApplication-shell.

Więcej informacji można uzyskać na stronie domowej projektu: http://code.google.com/webtoolkit/gettingstarted.html

W celu stworzenia projektu Eclipse najpierw musimy stworzyć taki projekt. Do tego celu posłużymy się skryptem o nazwie projectCreator. Aby stworzyć projekt o nazwie MyProject musimy wpisać w linii komend:

projectCreator -eclipse MyProject

a następnie, aby w naszym projekcie znajdował się projekt GWT posłużymy się składnią:

applicationCreator -eclipse MyProject com.mycompany.client.MyApplication

Po zakończeniu skrypt ten stworzy dodatkowo pliki .project i .classpath. Dzięki nim możemy teraz zaimportować ten projekt do Eclipsa (File->Import a następnie wybrać Existing Projects into Workspace).

Kiedy mamy już załadowany projekt, aby go uruchomić wystarczy wcisnąć zielony przycisk „Run”.

Komponenty

attachment:Diagram_komponentow_src.JPG

Nawigacja

Nawigacja w module webowym oparta jest na historii. Głównym miejscem przez które przechodzi każde żadanie jest obiekt klasy RtoWeb. Implementuje ona 2 interfejsy pakietu gwt:

W klasie tej w metodzie onHistoryChanged(String historyToken) w zależności od „historyToken” zostaje podmieniony layout strony oraz jej treść.

1 HistoryElement he = new HistoryElement(historyToken);
2 CompositeLazyInit.CompositeInfo layoutInfo =  layoutInfoList.find(he.getLayout());
3 RootPanel.get().remove(layoutWithHistory);
4 layoutWithHistory = (LayoutWithHistory) layoutInfo.getInstance();
5 RootPanel.get().add(layoutWithHistory);
6 layoutWithHistory.changeContent(he.getContent());

Klasa HistoryElement reprezentuje element historii. Zawiera w sobie metody do parsowania tokenów. W linijce 2 wyszukujemy layaut z listy layoutow dostepnych w naszej aplikacji. W linii 3 usuwamy stary layout. 4 – inicjalizujemy nowy layout. 5 – ustawiamy nowy layout. 6 – zmieniamy treść na danym layoucie na treść wynikającą z historyToken.

Layout’y

Wszystkie layouty dziedzicza po klasie abstrakcyjnej LayoutWithHistory. Główna metoda to changeContent(String token). Podmienia ona content na stronie na nowy. Sam układ strony, to gdzie ma się znaleźć dany content ustawiany jest w klasach dziedziczących po LayoutWithHistory.

Lazy Init

Aby aplikacja przy starcie nie tworzyła wszystkich dostepnych contentów oraz layoutów, przez co obciążała klienta, zastosowaliśmy lazy init. Wszystkie klasy dziedziczące po CompositeLazyInit są inicjalizowane w momencie kiedy ich potrzebujemy, a nie przy starcie aplikacji.

W klasie tej znajduje się statyczna abstrakcyjna klasa CompositeInfo. To dzieki niej uzyskujemy ten efekt.

public abstract static class CompositeInfo {
        private CompositeLazyInit instance;
        private String name, description;
        public CompositeInfo(String name, String desc) {
            this.name = name;
            description = desc;
        }
        public abstract CompositeLazyInit createInstance();
        public final CompositeLazyInit getInstance() {
            if (instance != null)
                return instance;
            return (instance = createInstance());
        }
    }

Obiekty dziedziczące po tej klasie musza zaimplementowac metodę createInstance(),która zwraca nam obiekt nadklasy CompositeLazyInit. Obiekt klasy CompositeLazyInit tworzony jest dopiero gdy wywołamy metodę getInstance(). Innymi słowy obiekt klasy CompositeInfo wie jak stworzyć obiekt CompositeLazyInit, ale go nie tworzy dopóki nie odpalimy metody getInstance(). We wszystkich listach w aplikacji trzymane są CompositeInfo i dopiero przez nie dostajemy się do obiektów CompositeLazyInit. A jak widać po kodzie sam obiekt CompositeInfo jest bardzo lekki bo zawiera w sobie jedynie dwa atrybuty name i description.

Diagram z layoutami i contentami:

attachment:lazy_init_diagram.JPG

Remote Procedure Call (RPC)

GWT umożliwia wymianę danych między klientem a serwerem bez konieczności przeładowania strony. Do tego celu wykorzystuje się RPC – Remote Procedure Calls. Dzięki temu mechanizmowi, możliwe jest umieszczenie całości interfejsu użytkownika po stronie klienta, po stronie serwera zaś znajdować się będą tzw. service’y, czyli implementacje metod wywoływanych przez klienta zdalnie.

Struktura klas składających się na RPC jest następująca:

attachment:rpc.JPG

Jak korzystać z RPC – na przykładzie LogowanieInterfejs

1. Ażeby stworzyć własną usługę, należy stworzyć dwa interfejsy, które znajdować się będą po stronie klienta.

public interface LogowanieInterfejs extends RemoteService{   
        public int Login(String name, String pass) ;
}

2. Następnym krokiem jest stworzenie interfejsu asynchronicznego, bazującego na poprzednim.

public interface LogowanieInterfejsAsync{
        public void Login(String name, String pass,AsyncCallback callback);
}

Interfejs ten wymaga podania przez klienta obiektu typu AsyncCallback, który zostanie poinformowany o zakończeniu działania asynchronicznego żądania. Jego metody muszą również być typu void. Parametry zwracane przez żądanie zostaną dostarczone do klienta poprzez obiekt callback.

3. Kiedy mamy już oba interfejsy, musimy stworzyć implementację metod w nich zawartych. Do tego celu tworzymy klasę (znajdującą się już po stronie serwera), która będzie dziedziczyć po RemoteServiceServlet i realizować nasz interfejs synchroniczny (implements LogowanieInterfejs).

public class LogowanieInterfejsImpl extends RemoteServiceServlet implements LogowanieInterfejs {
      @EJB
      private UserRemote userBean;

    public int Login (String name, String pass)
    {
         if (!(userBean.login(name, pass))) return -1;
         else
       return 1; 
    }
}

Jak widać taka konstrukcja pozwala nam na asynchroniczne wywołanie operacji dostarczanych przez EJB.

4. Ostatnią czynnością jest oczywiście faktyczne wywołanie zdalnej metody naszego service’u.

Do tego celu musimy stworzyć instancję naszego interfejsu wykorzystując GWT.create(). Następnie określamy ścieżke dostępu (ServiceDefTarget; wykorzystuje się przy tym nazwy z pliku konfiguracyjnego web.xml) Wreszcie tworzymy obiekt AsyncCallback, który powróci do klienta z odpowiednimi danymi i wywołujemy metodę.

Do tworzenia instancji service’u i określania ścieżki stworzyliśmy w naszym interfejsie synchronicznym wewnętrzną klasę statyczną.

public interface LogowanieInterfejs extends RemoteService{
    
     public static class App {
        private static LogowanieInterfejsAsync ourInstance = null;
        public static synchronized LogowanieInterfejsAsync getInstance() {
            if (ourInstance == null) {
                ourInstance = (LogowanieInterfejsAsync) GWT.create(LogowanieInterfejs.class);
                ((ServiceDefTarget)ourInstance).setServiceEntryPoint(GWT.getModuleBaseURL() + "pl.edu.pw.iem.rto.web.RtoWeb/Logowanie");
            }
            return ourInstance;
        }
    }
    
public int Login(String name, String pass) ;
}

Wywołanie metody Login wygląda następująco:

InterfejsSynchroniczny.KlasaWewnetrzna.getInstance().Metoda(param1,…,paramN, AsyncCallback callback);

Ponieważ opisywane tu wywołania metod są asynchroniczne, aplikacja nie będzie czekać na ich rezultat. Odpowiedzialność za to spada na obiekt callback, który zadecyduje co zrobić w zależności od powodzenia/niepowodzenia wywołania lub obiektu zwracanego przez zdalną metodę.

Do tego celu obiekt AsyncCallback wykorzystuje dwie metody:

W naszym przypadku wywołanie metody Login(login, pass) wyglądało następująco:

LogowanieInterfejs.App.getInstance().Login((log.textBoxLogin).getText(),(log.textBoxHaslo).getText(),new 
AsyncCallback() 
{                 
   public void onSuccess(Object result) {
   if (((Integer)result).intValue()==-1)  Window.alert("Niepoprawny login lub haslo");
   else
   {
 Sesja.setZalogowany(1);
HistoryUtils.go(new HistoryElement(LayoutWithMenu.init().getName(),ZglosPrzedmiot.init().getName()));
   }
  }

 
public void onFailure(Throwable caught) {Window.alert("Blad wewnetrzny");}
});
}

W powyższym przykładzie, kiedy RPC zakończy się powodzeniem sprawdzany jest obiekt zwracany przez zdalną metodę. Jeśli będzie to liczba -1, to znak, że proces logowania w sensie logiki biznesowej nie powiódł się (ale RPC został wykonany poprawnie); w przeciwnym wypadku jesteśmy zalogowani i następuje przekierowanie dalej. Jeśli zaś RPC się nie powiedzie, pojawia się komunikat o błędzie wewnętrznym.

Diagram klas wykorzystywanych przy wywoływaniu metod RPC:

attachment:servisy.JPG


2015-09-23 06:51