poniedziałek, 5 maj 2008

BigDecimal w GWT

Zawsze gdy testujemy jakiś framework wybieramy najprostszą drogę, co między innymi oznacza, iż korzystamy z takich typów danych jak: String i Integer. Niemniej jednak przychodzi czas kiedy musimy zmierzyć się z obsługą co najmniej kilku typów danych m.in. z typem zmiennoprzecinkowym. Doświadczenie nauczyło mnie, że jeżeli chodzi o Javę to najlepszym typem zmiennoprzecinkowym do wykonywania operacji arytmetycznych jest BigDecimal. Pewnie co niektórym nasuwa się pytanie - dlaczego nie double. Moją odpowiedzią będzie przykład zaczerpniety ze strony http://epramono.blogspot.com:

public static void main (String[] args) {
System.out.println (
"(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1) = " +
(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1));

double d = 0.0;
while (d <= 1.0) d += 0.1;
System.out.println ("d = " + d);

System.out.println ("0.0175 * 100000 = " + 0.0175 * 100000);
}
Wynik na wyjściu:
(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1) = 0.9999999999999999
d = 1.0999999999999999
0.0175 * 100000 = 1750.0000000000002
Upss... co za niemiła niespodzianka :(

Nie o tym jednak zamierzam pisać moim głównym tematem jest GWT (wersja 1.4.60), a w zasadzie obsługa typu BigDecimal w GWT, której najogólniej rzecz ujmując po prostu nie ma. To oczywiście poważny problem... ale jest na to rozwiązanie:
  • Wchodzimy na stronę: http://code.google.com/p/gwt-math/.
  • Ściągamy dwie biblioteki gwt-math-2.0.1.jar, gwt-math-server-2.0.1.jar i umieszczamy je w classpath naszego projektu.
  • W pliku MyModule.gwt.xml dodajemy sekcję:
    <inherits name="com.googlecode.gwt.math.Math"/>
  • Na tym koniec naszej pracy, możemy korzystać już z klas java.math.BigDecimal, java.math.BigInteger.

Reasumując: często jest tak, gdy sytuacja wydaje się być beznadziena z pomocą przychodzi Google, poprzez które odkrywamy, że społeczność Open Source przewidziała, że możemy natknąć się na dany problem. Stąd lepiej najpierw poszukać, niż zrobić po swojemu :)

piątek, 25 kwiecień 2008

GWT - internacjonalizacja

Jeżeli docelową grupą użytkowników naszej aplikacji są ludzie o różnych narodowościach wówczas warto dostosować ją do obsługi wielu języków. Pierwszą rzeczą z którą przyjdzie się nam zmierzyć będzie tłumaczenie tekstów stałych. Samo tłumaczenie tekstów możemy pozostawić lingwistom, natomiast naszym zadaniem jest napisanie tak aplikacji, aby obsługa tych tekstów była możliwa i przy dodawaniu kolejnych tłumaczeń nie wymagała pracy programistycznej.

Oczywiście każdy programista dość szybko poradziłby sobie z tym zagadnieniem wymyślając swój własny mechanizm, ale programista leniwy (czytaj: skuteczny) przegrzebie dokumentację w celu znalezieniu mechanizmu, który już został wymyślony. Jak to wygląda w GWT...

Podstawowym elementem, który umożliwi nam lokalizację tekstów stałych jest moduł zawarty w pakiecie com.google.gwt.i18n oferuje on kilka mechanizmów: Constants, Messages, ConstantsWithLookup, Dictionary, Localizable. Skoncentrujmy się jednak na dwóch podstawowych Constants i Messages, które powinny być używane w większości przypadków.

Jeżeli chcemy skorzystać z tych mechanizmów nasz moduł musi dziedziczyć po module com.google.gwt.i18n.I18N (plik MyModule.gwt.xml w pakiecie com.mycompany.mymodule):

<module>
<inherits name="com.google.gwt.i18n.I18N"/>
</module>
<extend-property name="locale" values="pl,en"/>
Kolejnym krokiem jest stworzenie plików zawierających teksty stałe dla poszczególnych języków oraz plik domyślny.

Plik domyślny MyConstans.properties (w pakiecie com.mycompany.mymodule.client):
helloWorld = Witaj Świecie
goodbyeWorld = Do widzenia Świecie
Plik dla języka polskiego MyConstans_pl.properties (w pakiecie com.mycompany.mymodule.client):
helloWorld = Witaj Świecie
goodbyeWorld = Do widzenia Świecie
Plik dla języka angielskiego MyConstans_en.properties (w pakiecie com.mycompany.mymodule.client):
helloWorld = Hello World
goodbyeWorld = Goodbye World
Kolejnym krokiem jest stworznie w pakiecie com.mycompany.mymodule.client interfejsu MyConstans, w którym nazwy metod będą odpowiadać kluczom w plikach z tekstami stałymi:
public interface MyConstants extends Constants {
String helloWorld();
String goodbyeWorld();
}
Aby użyć nasze teksty wystarczą dwie linie kodu w kodzie klasy klienta (np. MyModule.java):
  MyConstants myConstants = (MyConstants)GWT.create(MyConstants.class);
Window.alert(myConstants.helloWorld());

Drugi mechanizm, który niejako stawi rozszerzenie Constants to Messages, różni się on jedynie tym, iż teksty stałe mogą posiadać argumenty. Załóżmy, iż w pliku ErrorMessages.properties mamy tekst:

permissionDenied = Błąd {0}: Użytkownik {1} nie ma uprawnień dostęp do {2}
Wóczas definiujemy interfejs analogiczny do Constants przy czym metody powinny mieć tyle argumentów wejściowych ile zostało wymienonych w tekstach stałych. Definicja interfejsu ErrorMessages.java:
public interface ErrorMessages extends Messages {
String permissionDenied(int errorCode, String username, String resource);
}

Aby teksty stałe były wyświetlane w danym języku mamy dwie możliwości. Pierwsza polega na ustawieniu właściwości <meta name="gwt:property" content="locale=pl"> bezpośrednio w stronie html:

<html>
<head>
<meta name="gwt:module" content="com.mycompany.mymodule.MyModule">
<meta name="gwt:property" content="locale=pl">
</head>
<body>
<script src="gwt.js"></script>
</body>
</html>
Druga możliwość to dodanie parametru do URL:
http://www.mycompany.com/MyModule.html?locale=pl

Mechanizmy, ktore zostały opisane można używać tylko w kodzie na podstawie, którego jest generowany JavaScript (czyli który jest umieszczony w pakiecie client). Podczas kompilacji GWT wytwarza dla każdej lokalizacji odrębny plik JavaScript.

Reasumując GWT dostarcza dość kompleksowy mechanizm internacjonalizacji, choć według mnie wystarczyłby mechanizm Messages, bo przecież nie mogę od razu przewidzieć, że mój tekst stały nie będzie miał kiedyś argumentów. Kolejną rzeczą, która stanowi dla mnie pewną wadę jest to, iż nie można używać tych mechanizmów po stronie serwera, co zmusza nas do korzystania ze standardowego rozwiązania dostarczonego przez Java ResourceBundle.

poniedziałek, 21 styczeń 2008

Równy czy nierówny - oto jest pytanie

Ktoś kiedyś powiedział "mnie język w programowaniu nie przeszkadza"? Co mnie więcej oznacza, iż doświadczony programista, który miał do czynienia z kilkoma językami (np. C, Java) bez problemu powinien poradzić sobie z programowaniem w kolejnym języku (np. w C#). Jest w tym wiele prawdy, ale czy bez kompleksowej wiedzy z danego języka kod owego programisty będzie na pewno niezawodny?

Rozważmy prosty przykład napisany w języku Java:

Integer i1 = 1000;
Integer i2 = 1000;
if (i1 == i2) {
System.out.println("ten sam obiekt");
}
else {
System.out.println("rozne obiekty");
}
if (i1.equals(i2)) {
System.out.println("wartosci obiektow sa takie same");
}
else {
System.out.println("wartosci obiektow nie sa takie same");
}
Czy jest coś w tym przykładzie co mogłoby nas skłonić do refleksji - raczej nie. Oczywiste wydaje się, iż w pierwszym warunku zostanie wyświetlone na konsoli "rozne obiekty", natomiast w drugim warunku "rowne obiekty". Pierwszy warunek będzie spełniony tylko wtedy gdy będziemy mieli do czynienia ze zmiennymi, które wskazują dokładnie na ten sam obiekt. Drugi warunek to już porównanie wartości tych obiektów więc sprawa jest dość oczywista.

Zmodyfikujmy ten przykład:
Integer i1 = 10;
Integer i2 = 10;
if (i1 == i2) {
System.out.println("ten sam obiekt");
}
else {
System.out.println("rozne obiekty");
}
if (i1.equals(i2)) {
System.out.println("wartosci obiektow sa takie same");
}
else {
System.out.println("wartosci obiektow nie sa takie same");
}
Co się okazuje? Pierwszy warunek jest spełniony w efekcie na konsoli zostanie wyświetlone "ten sam obiekt". Dlaczego? Odpowiedź jest prosta, chociaż nie oczywista: w języku Java w celu zaoszczędzenia pamięci instancje klas Short and Integer o wartościach od -128 do 127 wskazują na ten sam obiekt (obszar pamięci). Inaczej się ma sytuacja jeżeli jawnie użyjemy operatora new:
Integer i1 = new Integer(10);
Integer i2 = new Integer(10);
if (i1 == i2) {
System.out.println("ten sam obiekt");
}
else {
System.out.println("rozne obiekty");
}
if (i1.equals(i2)) {
System.out.println("wartosci obiektow sa takie same");
}
else {
System.out.println("wartosci obiektow nie sa takie same");
}
Wówczas zostanie wyświetlone "rozne obiekty".

Wniosek z tego jest jeden, iż bez dogłębnej znajomości języka, konstrukcje, które mogłyby się wydawać oczywiste mogą nas zaskoczyć w najmniej oczekiwanym momencie. Jest na to jednak rada: jeżeli jesteś czegoś pewien sprawdź to, a jeżeli jesteś czegoś bardzo pewien sprawdź to dwa razy...

niedziela, 30 grudzień 2007

Kreatory w GWT

W dystrybucji GWT znajdują się cztery skrypty cmd, które w największym skrócie można nazwać kreatorami. Każdy z nich jest wykorzystywany na różnym etapie tworzenia aplikacji.

projectCreator
GWT pozwala na łatwą integrację ze środowiskiem Eclipse, dlatego też jeżeli podczas tworzenia aplikacji programista zamierza korzystać z tego IDE, wówczas powinien użyć tego narzędzia. Kreator ten zakłada strukturę katalogów (wraz z plikami .project oraz .classpath) , która pozwoli łatwo zaimportować tworzoną aplikację jako projekt Eclipse.

Wykonując komendę:

projectCreator -eclipse MyAppPrj -out MyAppDir
Utworzona zostanie następująca struktura:
Created directory MyAppDir\src
Created directory MyAppDir\test
Created file MyAppDir\.project
Created file MyAppDir\.classpath
applicationCreator
Kreator ten pełni trzy funkcje:

  • Wytwarza strukturę pakietów dla aplikacji.
  • Generuje domyślne pliki HTML i Java spięte w prostym pliku module XML. Te pliki stanowią prostą aplikację od której można zacząć budowanie bardziej zaawansowanych funkcjonalności.
  • Tworzy skrypty cmd, które pozwalają przekompilować i uruchomić aplikację w trybie hosted mode.
Wykonując komendę (bez Eclipse):
applicationCreator -out MyAppDir com.example.client.MyApp
Wykonując komendę (z Eclipse):
applicationCreator -eclipse MyAppPrj -out MyAppDir com.example.client.MyApp
Utworzona zostanie następująca struktura:
Created directory MyAppDir\src\com\example
Created directory MyAppDir\src\com\example\client
Created directory MyAppDir\src\com\example\public
Created file MyAppDir\src\com\example\MyApp.gwt.xml
Created file MyAppDir\src\com\example\public\MyApp.html
Created file MyAppDir\src\com\example\client\MyApp.java
Created file MyAppDir\MyApp-shell.cmd
Created file MyAppDir\MyApp-compile.cmd
W przypadku parametru -eclipse dodatkowo powstanie:
Created file MyAppDir\MyApp.launch
i18nCreator
Jeżeli pojawia się skrót i18n to oczywiście chodzi o internacjonalizację. Tym kreatorem wytwarzamy proste pliki properties zawierające teksty stałe w formie klucz/wartość. Generowany jest również skrypt cmd [Appl]-i18n, który na podstawie plików properties generuje interfejs Java poprzez który mamy dostęp do tekstów stałych z kodu Java aplikacji GWT.

Wykonując komendę (bez Eclipse):
i18nCreator -out MyAppDir com.example.client.MyAppConstants
Wykonując komendę (z Eclipse):
i18nCreator -eclipse MyAppPrj -out MyAppDir com.example.client.MyAppConstants
Utworzona zostanie następująca struktura:
Created file MyAppDir\src\com\example\client\MyAppConstants.properties
Created file MyAppDir\MyAppConstants-i18n.cmd
W przypadku parametru -eclipse dodatkowo powstanie:
Created file MyAppDir\MyAppConstants-i18n.launch
jUnitCreator
Tworzy odpowiedni katalog oraz pliki, w którym należy umieścić testy jednostkowe aplikacji GWT.

Wykonując komendę (bez Eclipse):
junitCreator -junit c:\junit.jar -module com.example.client.MyApp -out MyAppDir com.example.client.MyAppTest
Wykonując komendę (z Eclipse):
junitCreator -junit c:\junit.jar -eclipse MyAppPrj -module com.example.client.MyApp -out MyAppDir com.example.client.MyAppTest
Utworzona zostanie następująca struktura:
Created directory MyAppDir\test\com\example\client\test
Created file MyAppDir\test\com\example\client\test\MyAppTest.java
Created file MyAppDir\MyAppTest-hosted.cmd
Created file MyAppDir\MyAppTest-web.cmd
W przypadku parametru -eclipse dodatkowo powstanie:
Created file MyAppDir\MyAppTest-hosted.launch
Created file MyAppDir\MyAppTest-web.launch
Dzięki kreatorom można w ciągu dosłownie kilku chwil zbudować swoją pierwsza aplikację w GWT, dzięki czemu nawet niecierpliwi się nie zniechęcą. Oczywiście bardziej istotne jest to, że nie trzeba wykonywać powtarzalnych czynności, które może za nas wykonać automat.