środa, 25 marca 2009

Wykorzystanie obiektów z warstw niższych w warstwie WEB GWT.

Jednym z problemów jaki miałem w korzystaniu z GWT było wykorzzystanie tych samych obiektów na poziomie warstwy logiki biznesowej, czy DAO oraz na poziomie warstwy WEB. Teoretycznie są to obiekty POJO, więc problemów być nie powinno, jednak wymaganie, z drugiej strony ich deklaracje znajdują się w module web (a więc w większości przypadków w plikach war).
Okazuje się jednak, że tak być nie musi. Klasy muszą się znajdować w katalogu GWT (w ktatlogu client) w czasie kompilacji kodu GWT, ale później można je przenieść w dowolne miejsce w aplikacji.

Muszą zostać jednak spełnione dwa warunki:
  • Pakiet w którym znajduje się klasa musi być nadal ten sam.
  • Moduł web musi mieć dostęp do skompilowanych źródeł.

W związku z tym struktura, w której całość działała w mojej aplikacji wyglądała tak:

EAR
  • dao.jar
  • business.jar
  • web.war
  • META-INF
WAR
  • images
  • css
  • WEB-INF
  • WEB-INF -> lib -> web.jar
  • META-INF
  • [...].js
  • [...].html
Co istotne, obiekty zawarte były w dao.jar a nie w web.jar.

Oczywiście tego typu struktura wymaga aby biblioteka GWT była importowana w module DAO (ze względu na interfejs IsSerializable), ale jest to niewielkie poświęcenie za możliwość korzystania z tych samych obiektów w module GWT oraz w modulach biznesowych. Dzięki takiemu rozwiązaniu mogę załadować obiekt, na przykład przy pomocy JPA z bazy a następnie przekazać go bezpośrednio do warstwy webowej.

Kompilacja
Całość została przezemnie zautomatyzowana przy pomocy skryptów antowych.
Standardowo obiekty są umieszczone w module DAO.
Przed kompilacją Obiekty są przenszone do modułu GWT.

<move todir="dao/src/pl/project/client/objects">
<fileset dir="webui/src/pl/project/client/objects"/>
</move>


Ant, kompiluje moduł GWT przy pomocy taska antowego dostępnego w bibliotece ant-gwt.

<taskdef resource="dk/contix/ant/gwt/ant-gwt.xml"
classpathref="project.classpath"/>
<gwtcompile destdir="${build.dir}/gwt-web" optimize="true">
<fileset dir="${module.web.basedir}/web/src">
<include name="**/*.gwt.xml"/>
</fileset>
</gwtcompile>

Przenosze spowrotem obiekty do DAO

<move todir="web/src/pl/project/client/objects">
<fileset dir="dao/src/pl/project/client/objects"/>
</move>


Kompiluje pozostałe elementy a następnie składam tworze pliki JAR, WAR oraz EAR

Warto wspomnieć, że GWT nie miało problemów z anotacjami.

Następnym razem napisze jak wymusić na GWT aby wywoływało bezpośrednio Beana EJB (na przyklad Stateless Session Bean), dzięki czemu wszystkie operacje wykonywane są wewnątrz transakcji.

poniedziałek, 23 marca 2009

Obsługa wyjątków w GWT

Sprawa jest stosunkowo prosta od momentu gdy powstało GWT 1.5. Mechanizm RPC zastosowany w GWT pozwala na przekazywanie wyjątków powstałych w warstwach niższych, jednak te wyjątki muszą spełniać pewne warunki.

Wyjątek musi dziedziczyć po Throwable (a więc na przykład Exception). Musi implementować java.io.Serializable oraz co najważniejsze musi implementować com.google.gwt.user.client.rpc.IsSerializable. Ta ostatnia klasa jest wymagana dla przekazywania wszelkich obiektów poprzez RPC, a więc dotyczy to również wyjątków.

Wyjątek jest przechwytywany w metodzie public void onFailure(Throwable caught) i zawarty jest w zmiennej caught. Dalszą obsługę wyjątku możemy robić już w standardowy sposób.

Przykładowy kod:

Klasa błędu

import com.google.gwt.user.client.rpc.IsSerializable;
import java.io.Serializable;

public class MyException extends Exception
implements Serializable, IsSerializable {
String name = getClass().getName();

public String getName() {
return name;
}
}


Obsługa błedu

MyAppService.App.getInstance().method(new AsyncCallback<Void>() {
public void onFailure(Throwable caught) {
if (caught instanceof MyException)
handleException((MyException)caught);
else
handleUnknownException(caught);
}

public void onSuccess(Void result) {
[...]
}
});


Deklaracja metody w MyAppAsync

public interface MyAppAsync(){
void method(AsyncCallback<Void> async);
}


Niestety to rozwiązanie posiada dwa poważne problemy.
Po pierwsze pozwala na przesłanie tylko własnych klas błędów.
Po drugie wymaga aby klasa błędu była w pakiecie client w module zawierającym kod GWT. Oznacza to poważny problem w zależnościach między modułami.

Rozwiązanie drugiego problemu w skrócie:
Jest możliwa kompilacja modułu GWT przy pomocy kompilatora GWT (do postaci javascript), a następnie kompilacja źródeł z innym modułem. To może być na przykład moduł trzymający wszystkie obiekty. Istotne jest tylko, aby moduł GWT posiadał dostęp do tej skompilowanej klasy oraz klasa znajdowała się w odpowiednim pakiecie.

Ten konkretny problem postaram się opisać szerzej następnym razem.

Wstęp

Postanowiłem stworzyć tego bloga, aby podzielić się z szerszym gronem moimi frustracjami oraz spostrzeżeniami. Może ktoś, dzięki moim spostrzeżeniom w przyszłości uniknie błędów, z którymi ja się spotkałem.