Dopiero co wróciłem z konferencji Netbeans Day w Poznaniu. Konferencja była na naprawdę bardzo wysokim poziomie, z 6 prezentacji 4 bardzo mi się podobały.
Większość prezentacji traktowała o Netbeans Rich Client Platform. Temat raczej nie przyda mi się w praktyce, ale i tak było bardzo ciekawie.
Adam Bien pokazał prawdziwy show z Netbeansem w roli głównej. Nie wiedziałem że to narzędzie ma aż takie możliwości, będę musiał częściej go używać. Najfajniejsze jest to, że ma zintegrowane różne pożyteczne funkcje w jednym narzędziu, podgląd bazy danych (naprawdę fajny, tego akurat używam od dawna), serwera JEE, deployment za pomocą jednego kliknięcia, tworzenie producentów i konsumentów JMS, wsparcie dla JSF, długo by wymieniać. No i coś czego "out of the box" nie ma ani Eclipse ani IDEA - UML i reverse engineering.
Dziwi mnie że było tak mało ludzi. Sala nie była duża, a nie była wypełniona chyba nawet w połowie. Może jest to spowodowane tym że Netbeans jest wciąż mało popularnym środowiskiem. Sam nie używam go jako swojego podstawowego IDE, ale to znaczy że go za dobrze nie znam, więc tym bardziej warto go poznać. Cóź, Ci co nie przyjechali dużo stracili, namawiam do wybrania się na tę konferencję przy następnej edycji, ja na pewno pojadę.
Sunday, October 26, 2008
Sunday, September 14, 2008
Nadać plikowi takie same prawa dla grupy jakie ma dla właściciela - skrypcik w Rubym
Poniżej prosty (w miarę) skrypcik w rubym, nadający plikom takie same uprawnienia dla grupy, jak dla właściciela. Przydatne jeśli chcemy korzystać z Subversion za pomocą protokołu svn+ssh - wtedy użytkownik musi mieć odpowiednie uprawnienia do plików w repozytorium.
sudo script.rb /home/svn/repository
def give_rights_of_owner_to_group(file_path)
s = File::Stat.new((file_path))
rights = sprintf('%o', s.mode)
all = rights[-1,1]
group = rights[-2,1]
owner = rights[-3,1]
beginning = rights[0..-4]
new_rights = "#{beginning}#{owner}#{owner}#{all}".to_i 8
puts "#{(file_path)}: #{rights} #{sprintf('%o', new_rights)}"
File.chmod(new_rights, file_path)
end
def chmod_all_files_in_dir(dir_path)
Dir.open(dir_path) do |d|
d.each do |fn|
if not fn =~ /^(\.\.?)$/
file_path = File.expand_path(d.path + File::Separator + fn)
give_rights_of_owner_to_group file_path
if File.directory? file_path
chmod_all_files_in_dir file_path
end
end
end
end
end
if $*.empty?
chmod_all_files_in_dir(".")
else
chmod_all_files_in_dir($*[0])
end
Używa się podając jako parametr katalog w którym chcemy zmienić uprawnienia (czyli katalog repozytorium Subversion). Uruchomione bez parametru zmienia uprawnienia w bierzącym katalogu. Oczywiście żeby zmienić uprawnienia do pliku którego nie jesteśmy właścicielem musimy skrypt uruchamiać z prawami roota. Czyli uruchamiamy tak:
sudo script.rb /home/svn/repository
Wednesday, September 3, 2008
Instalacja subversion pod ubuntu (lub innym linuxem)
Zainstalowanie subversion pod ubuntu jest stosunkowo proste. Przede wszystkim trzeba ściągnąć pakiet svn
sudo apt-get install subversion
Subversion zainstalowane, teraz trzeba utworzyć repozytorium. Zanim to jednak zrobimy, warto utworzyć użytkownika specjalnie dla subversion.
sudo adduser svn
Teraz możemy już utworzyć repozytorium. Najlepiej od razu jako użytkownik svn.
sudo -u svn mkdir /home/svn/repository
sudo -u svn svnadmin create /home/svn/repository
Ok, mamy repozytorium. Dostać się do niego można na kilka sposobów, używając różnych protokołów.
file://
Dostęp bezpośrednio przez system plików. Aby go uzyskać wystarczy komenda
svn info file:///home/svn/repository
svn to wywołanie klienta subversion, info mówi że chcemy tylko ściągnąć informacje o danym repozytorium, a to co później to protokół i adres repozytorium.
Kontrola dostępu na poziomie dostępu do plików. Tzn. jeśli user który odpala tę komendę ma dostęp do odczytu plików w repozytorium, to może je odczytać, jeśli ma uprawnienia także do zapisu, to może zapisać.
svn://
To jest specjalny protokół subversion. Nieszyfrowany. Aby połączyć się z serwerem należy go najpierw uruchomić:
sudo -u svn svnserve -d -r /home/svn/repository
sudo -u svn jest po to, żeby serwer uruchomić jako użytkownik svn. Musi on być uruchomiony przez użytkownika, który ma dostęp do plików zarówno do odczytu jak i zapisu.
-d oznacza że uruchamiamy serwer w trybie deamon
-r to położenie naszego repozytorium w systemie plików
I teraz już możemy się odwołać do repozytorium przez klienta svn:
svn info svn://localhost
Co bardziej dociekliwi zauważą, że tutaj nie trzeba podawać ścieżki do repozytorium. Wystarczy, że podaliśmy ją serwerowi, a on już na odpowiednim porcie "podaje" co trzeba.
Tutaj dostęp kontrolujemy za pomocą mechanizmów Subversion. W katalogu /home/svn/repository/conf znajduje się plik svnserve.conf
Możemy odkomentować linijki
# anon-access = read
# auth-access = write
Są to domyślne ustawienia oznaczające odczyt dla każdego, zapis tylko dla zautoryzowanych użytkowników. Możliwe wartości to read, write i none. Żeby użytkownicy mogli się logować, musimy jeszcze odkomentować linijkę
# password-db = passwd
Jest to wskazanie na plik, w którym trzymane są nazwy i hasła użytkowników. Jeśli ścieżka nie jest podana, odnosi się do katalogu conf. Nazwy i hasła w tym pliku trzymane są w czystym tekście, w postaci niezaszyfrowanej, więc ważne jest odpowiednie ustawienie dostępu do tego pliku.
svn+ssh://
Tak jak wyżej, z tą różnicą, że połączenie jest szyfrowane i nie potrzebujemy startować serwera. Poza tym nie jest używany plik passwd. Są jednak używane ustawienia anon-access i auth-access. W tym przypadku zautoryzowany użytkownik to taki, któremu udało się zautoryzować przez ssh.
http://
Aby udostępnić repozytorium Subversion przez HTTP potrzebny jest serwer Apache. Instalujemy więc apache'a:
sudo apt-get install apache2 libapache2-svn
Teraz należy odpowiednio zmodyfikować plik /etc/apache2/mods-enabled/dav_svn.conf Modyfikacje polegają głównie na odkomentowaniu poszczególnych linii:
<Location /svn>
DAV svn
#Tutaj ścieżka do naszego repozytorium
SVNPath /home/svn/repository
#Konfiguracja sposobu autoryzacji
AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /etc/apache2/dav_svn.passwd
#Dzięki tym trzem liniom każdy ma dostęp do odczytu, ale tylko
#zautoryzowani użytkownicy do zapisu
<LimitExcept GET PROPFIND OPTIONS REPORT>
Require valid-user
</LimitExcept>
</Location>
Serwer skonfigurowany, dostęp jako tako też, teraz należałoby ustawić plik /etc/apache2/dav_svn.passwd. Plik ten podaliśmy powyżej jako plik z hasłami. Aby dodać do niego użytkownika, należy wykonać komendę
htpasswd /etc/apache2/dav_svn.paswd franek
System zapyta jeszcze o hasło dla franka, i użytkownik franek zostanie dodany do pliku. Od tej chwili będzie mógł autoryzować swój dostęp do naszego repozytorium subversion.
Serwer apache uruchamiany jest z prawami użytkownika www-data. Nie ma on dostępu do naszego repozytorium, więc musimy mu taki dostęp nadać. Możemy to zrobić na dwa sposoby:
1. Dodać użytkownika www-data do grupy svn i dać poszczególnym plikom i katalogom w repozytorium możliwość zapisu dla grupy
2. Zmienić właściciela repozytorium na www-data.
Użyjemy tutaj drugiego sposobu ze względu na jego prostotę ;)
sudo chown -R www-data:www-data /home/svn
Teraz tylko zrestartować apache'a
sudo /etc/init.d/apache2 restart
I możemy korzystać z repozytorium pod adresem http:///svn
https://
Dostęp szyfrowany przez http jest taki sam jak dostęp nieszyfrowany, z tym że do apache'a trzeba dodać certyfikat SSL.
sudo apt-get install subversion
Subversion zainstalowane, teraz trzeba utworzyć repozytorium. Zanim to jednak zrobimy, warto utworzyć użytkownika specjalnie dla subversion.
sudo adduser svn
Teraz możemy już utworzyć repozytorium. Najlepiej od razu jako użytkownik svn.
sudo -u svn mkdir /home/svn/repository
sudo -u svn svnadmin create /home/svn/repository
Ok, mamy repozytorium. Dostać się do niego można na kilka sposobów, używając różnych protokołów.
file://
Dostęp bezpośrednio przez system plików. Aby go uzyskać wystarczy komenda
svn info file:///home/svn/repository
svn to wywołanie klienta subversion, info mówi że chcemy tylko ściągnąć informacje o danym repozytorium, a to co później to protokół i adres repozytorium.
Kontrola dostępu na poziomie dostępu do plików. Tzn. jeśli user który odpala tę komendę ma dostęp do odczytu plików w repozytorium, to może je odczytać, jeśli ma uprawnienia także do zapisu, to może zapisać.
svn://
To jest specjalny protokół subversion. Nieszyfrowany. Aby połączyć się z serwerem należy go najpierw uruchomić:
sudo -u svn svnserve -d -r /home/svn/repository
sudo -u svn jest po to, żeby serwer uruchomić jako użytkownik svn. Musi on być uruchomiony przez użytkownika, który ma dostęp do plików zarówno do odczytu jak i zapisu.
-d oznacza że uruchamiamy serwer w trybie deamon
-r to położenie naszego repozytorium w systemie plików
I teraz już możemy się odwołać do repozytorium przez klienta svn:
svn info svn://localhost
Co bardziej dociekliwi zauważą, że tutaj nie trzeba podawać ścieżki do repozytorium. Wystarczy, że podaliśmy ją serwerowi, a on już na odpowiednim porcie "podaje" co trzeba.
Tutaj dostęp kontrolujemy za pomocą mechanizmów Subversion. W katalogu /home/svn/repository/conf znajduje się plik svnserve.conf
Możemy odkomentować linijki
# anon-access = read
# auth-access = write
Są to domyślne ustawienia oznaczające odczyt dla każdego, zapis tylko dla zautoryzowanych użytkowników. Możliwe wartości to read, write i none. Żeby użytkownicy mogli się logować, musimy jeszcze odkomentować linijkę
# password-db = passwd
Jest to wskazanie na plik, w którym trzymane są nazwy i hasła użytkowników. Jeśli ścieżka nie jest podana, odnosi się do katalogu conf. Nazwy i hasła w tym pliku trzymane są w czystym tekście, w postaci niezaszyfrowanej, więc ważne jest odpowiednie ustawienie dostępu do tego pliku.
svn+ssh://
Tak jak wyżej, z tą różnicą, że połączenie jest szyfrowane i nie potrzebujemy startować serwera. Poza tym nie jest używany plik passwd. Są jednak używane ustawienia anon-access i auth-access. W tym przypadku zautoryzowany użytkownik to taki, któremu udało się zautoryzować przez ssh.
http://
Aby udostępnić repozytorium Subversion przez HTTP potrzebny jest serwer Apache. Instalujemy więc apache'a:
sudo apt-get install apache2 libapache2-svn
Teraz należy odpowiednio zmodyfikować plik /etc/apache2/mods-enabled/dav_svn.conf Modyfikacje polegają głównie na odkomentowaniu poszczególnych linii:
DAV svn
#Tutaj ścieżka do naszego repozytorium
SVNPath /home/svn/repository
#Konfiguracja sposobu autoryzacji
AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /etc/apache2/dav_svn.passwd
#Dzięki tym trzem liniom każdy ma dostęp do odczytu, ale tylko
#zautoryzowani użytkownicy do zapisu
<LimitExcept GET PROPFIND OPTIONS REPORT>
Require valid-user
</LimitExcept>
</Location>
Serwer skonfigurowany, dostęp jako tako też, teraz należałoby ustawić plik /etc/apache2/dav_svn.passwd. Plik ten podaliśmy powyżej jako plik z hasłami. Aby dodać do niego użytkownika, należy wykonać komendę
htpasswd /etc/apache2/dav_svn.paswd franek
System zapyta jeszcze o hasło dla franka, i użytkownik franek zostanie dodany do pliku. Od tej chwili będzie mógł autoryzować swój dostęp do naszego repozytorium subversion.
Serwer apache uruchamiany jest z prawami użytkownika www-data. Nie ma on dostępu do naszego repozytorium, więc musimy mu taki dostęp nadać. Możemy to zrobić na dwa sposoby:
1. Dodać użytkownika www-data do grupy svn i dać poszczególnym plikom i katalogom w repozytorium możliwość zapisu dla grupy
2. Zmienić właściciela repozytorium na www-data.
Użyjemy tutaj drugiego sposobu ze względu na jego prostotę ;)
sudo chown -R www-data:www-data /home/svn
Teraz tylko zrestartować apache'a
sudo /etc/init.d/apache2 restart
I możemy korzystać z repozytorium pod adresem http://
Dostęp szyfrowany przez http jest taki sam jak dostęp nieszyfrowany, z tym że do apache'a trzeba dodać certyfikat SSL.
Wednesday, August 27, 2008
Zostań dawcą szpiku - to nie takie straszne jak się wydaje
Dzisiaj nie będzie o javie. Nie będzie też o komputerach, informatyce, elektronice, nawet o fizyce albo matematyce nic nie będzie.
Jak zakładałem tego bloga to postanowiłem że będzie tylko o javie, programowaniu i pochodnych, ale co tam, mój blog i mogę sobie z nim robić co mi się podoba ;) Więc dzisiaj właśnie łamię tę zasadę. W celu jak najbardziej szczytnym, mianowicie propagowania idei "przeszczepiania" szpiku.
Właśnie, "przeszczep". "Przeszczepić szpik kostny", "zostać dawcą szpiku" - brzmi dość drastycznie, co? Brzmi jakby mieli nam otworzyć nogę, kość, i wysysać z niej szpik? Otóż nic bardziej mylnego!
Szpik pobiera się na dwa sposoby. Jeden to zwykłe pobranie krwi - normalnie igła w żyłę, jak na stacji krwiodawstwa czy u lekarza. We krwi mamy komórki krwiotwórcze, i to wystarcza. Z nich organizm biorcy zbuduje sobie swój zdrowy szpik na miejsce starego, chorego. Tak mi się przynajmniej wydaje, jeśli się mylę to liczę że ktoś lepiej zorientowany mnie poprawi.
Drugi sposób (1% zabiegów) polega rzeczywiście na pobraniu szpiku z kości. W znieczuleniu ogólnym wbija się dwie igły w udo. Następnego dnia (albo nawet jeszcze tego samego) wychodzimy ze szpitala. Zostają tylko dwie małe dziurki. To wszystko. Koniec. "Mission accomplished". Uratowałeś życie.
Więcej informacji na stronach:
http://www.poltransplant.org.pl/crndsikp.html
http://www.szpik.info/index.php
http://commed.pl/zostan-dawca-szpiku-kostnego-vt12799.html
A byłbym zapomniał. Do napisania tego posta skłoniła mnie dość szokująca informacja. Otóż ponad 90% polskich chorych uratowanych dzięki przeszczepowi szpiku dostało szpik od obywateli Niemiec. Dlaczego? Bo Niemcy mają w swoim rejestrze zarejestrowanych 3 mln. dawców, a Polska... 40 tys.
Przy najbliższej okazji (znaczy przy najbliższym oddawaniu krwi, hehe) idę się zarejestrować.
Jak zakładałem tego bloga to postanowiłem że będzie tylko o javie, programowaniu i pochodnych, ale co tam, mój blog i mogę sobie z nim robić co mi się podoba ;) Więc dzisiaj właśnie łamię tę zasadę. W celu jak najbardziej szczytnym, mianowicie propagowania idei "przeszczepiania" szpiku.
Właśnie, "przeszczep". "Przeszczepić szpik kostny", "zostać dawcą szpiku" - brzmi dość drastycznie, co? Brzmi jakby mieli nam otworzyć nogę, kość, i wysysać z niej szpik? Otóż nic bardziej mylnego!
Szpik pobiera się na dwa sposoby. Jeden to zwykłe pobranie krwi - normalnie igła w żyłę, jak na stacji krwiodawstwa czy u lekarza. We krwi mamy komórki krwiotwórcze, i to wystarcza. Z nich organizm biorcy zbuduje sobie swój zdrowy szpik na miejsce starego, chorego. Tak mi się przynajmniej wydaje, jeśli się mylę to liczę że ktoś lepiej zorientowany mnie poprawi.
Drugi sposób (1% zabiegów) polega rzeczywiście na pobraniu szpiku z kości. W znieczuleniu ogólnym wbija się dwie igły w udo. Następnego dnia (albo nawet jeszcze tego samego) wychodzimy ze szpitala. Zostają tylko dwie małe dziurki. To wszystko. Koniec. "Mission accomplished". Uratowałeś życie.
Więcej informacji na stronach:
http://www.poltransplant.org.pl/crndsikp.html
http://www.szpik.info/index.php
http://commed.pl/zostan-dawca-szpiku-kostnego-vt12799.html
A byłbym zapomniał. Do napisania tego posta skłoniła mnie dość szokująca informacja. Otóż ponad 90% polskich chorych uratowanych dzięki przeszczepowi szpiku dostało szpik od obywateli Niemiec. Dlaczego? Bo Niemcy mają w swoim rejestrze zarejestrowanych 3 mln. dawców, a Polska... 40 tys.
Przy najbliższej okazji (znaczy przy najbliższym oddawaniu krwi, hehe) idę się zarejestrować.
Sunday, August 10, 2008
"JBoss Seam Simplicity and Power Beyond Java EE" - recenzja
Dzięki Leszkowi Gruchale i Szczecińskiemu JUGowi, dostałem książkę "JBoss Seam Simplicity and Power Beyond Java EE" Michaela Juntao Yuana i Thomasa Heute. Przeczytałem ją, pora się zrewanżować i napisać recenzję.
"JBoss Seam..." to bardzo dobra książka. Daje wiedzę potrzebną do pisania aplikacji w tym frameworku, dołączonych jest wiele przykładów. Na początku autorzy wyjaśniają czym jest Seam, a jest co wyjaśniać, bo ten framework dość mocno różni się od innych o podobnych zastosowaniach. Wiedza w książce przedstawiona jest w jasny, zrozumiały sposób, za to duży plus.
Podczas programowania w Seamie, jak wszędzie, możemy napotkać na problemy. Debugowanie kodu samego frameworku nie jest raczej najlepszym pomysłem. Jest on skomplikowany i pisany z myślą właśnie o tym, żeby go używać, nie debugować. Książka opisuje sposoby "debugowania" wbudowane w sam framework, tak zwane "debug pages". Bardzo użyteczna funkcjonalność, pozwala nie tylko zobaczyć stacktrace, ale także stan sesji czy drzewo komponentów.
Jest też rozdział poświęcony procesom i regułom biznesowym. Ich obsługa jest wbudowana w Seam. Aby przedstawić co to takiego, w książce jako przykład jest system ticketów. Użytkownik się loguje, ma listę zadań, wybiera któreś z nich i od tego momentu jest ono do niego przydzielone. Otóż nie musimy zapisywać nigdzie w bazie danych że dane zadanie przydzielone jest do danego użytkownika, wystarczy że nadamy komponentowi Ticket scope = BUSINESS_PROCESS, o resztę zadba Seam.
Jest rozdział poświęcony testowaniu, które także framework mocno wspiera przez dostarczanie narzędzi pozwalających robić w testach to, co normalnie robi kontener (wstrzykiwanie zależności, "mockowanie" bazy danych, transakcji itp.).
W innym rozdziale opisane są sposoby uruchamiania aplikacji Seamowych w kontenerach nie dających funkcjonalności EJB3, np. Tomcat.
Brakowało mi w tej książce dwóch rzeczy. Zaznaczam jednak, że nie uważam tego za jej wady. Jedna rzecz to więcej informacji na temat samego EJB3 i JSF. Autorzy zakładają, że takie informacje czytelnik już posiada, ja jadnak wolałbym żeby więcej ich zawarli w książce. Rzecz druga to tzw. "internals", czyli to tam jest "w środku". Jak już wspomniałem Seam został raczej napisany z założeniem że taka wiedza nie jest użytkownikowi potrzebna, i dlatego też pewnie nie znalazła się w książce, jednak ja takie rzeczy po prostu lubię wiedzieć.
Polecam tę książkę każdemu kto chciałby pisać aplikacje w Seamie, a jeszcze nie wie jak to się robi.
"JBoss Seam..." to bardzo dobra książka. Daje wiedzę potrzebną do pisania aplikacji w tym frameworku, dołączonych jest wiele przykładów. Na początku autorzy wyjaśniają czym jest Seam, a jest co wyjaśniać, bo ten framework dość mocno różni się od innych o podobnych zastosowaniach. Wiedza w książce przedstawiona jest w jasny, zrozumiały sposób, za to duży plus.
Podczas programowania w Seamie, jak wszędzie, możemy napotkać na problemy. Debugowanie kodu samego frameworku nie jest raczej najlepszym pomysłem. Jest on skomplikowany i pisany z myślą właśnie o tym, żeby go używać, nie debugować. Książka opisuje sposoby "debugowania" wbudowane w sam framework, tak zwane "debug pages". Bardzo użyteczna funkcjonalność, pozwala nie tylko zobaczyć stacktrace, ale także stan sesji czy drzewo komponentów.
Jest też rozdział poświęcony procesom i regułom biznesowym. Ich obsługa jest wbudowana w Seam. Aby przedstawić co to takiego, w książce jako przykład jest system ticketów. Użytkownik się loguje, ma listę zadań, wybiera któreś z nich i od tego momentu jest ono do niego przydzielone. Otóż nie musimy zapisywać nigdzie w bazie danych że dane zadanie przydzielone jest do danego użytkownika, wystarczy że nadamy komponentowi Ticket scope = BUSINESS_PROCESS, o resztę zadba Seam.
Jest rozdział poświęcony testowaniu, które także framework mocno wspiera przez dostarczanie narzędzi pozwalających robić w testach to, co normalnie robi kontener (wstrzykiwanie zależności, "mockowanie" bazy danych, transakcji itp.).
W innym rozdziale opisane są sposoby uruchamiania aplikacji Seamowych w kontenerach nie dających funkcjonalności EJB3, np. Tomcat.
Brakowało mi w tej książce dwóch rzeczy. Zaznaczam jednak, że nie uważam tego za jej wady. Jedna rzecz to więcej informacji na temat samego EJB3 i JSF. Autorzy zakładają, że takie informacje czytelnik już posiada, ja jadnak wolałbym żeby więcej ich zawarli w książce. Rzecz druga to tzw. "internals", czyli to tam jest "w środku". Jak już wspomniałem Seam został raczej napisany z założeniem że taka wiedza nie jest użytkownikowi potrzebna, i dlatego też pewnie nie znalazła się w książce, jednak ja takie rzeczy po prostu lubię wiedzieć.
Polecam tę książkę każdemu kto chciałby pisać aplikacje w Seamie, a jeszcze nie wie jak to się robi.
Monday, July 7, 2008
Mockito
Dzisiaj chciałbym przedstawić Wam ciekawą bibliotekę do mocków. Na początek jednak pokrótce opiszę, co to takiego te mocki.
Mock - z angielskiego "udawać", "imitować". W programowaniu "mock object" oznacza obiekt, który udaje inny obiekt. Najlepiej chyba będzie wyjaśnić to na przykładzie.
Załóżmy, że mamy klasę Caller, realizującą połączenia telefoniczne, i klasę Phone, reprezentującą telefon (numer) na który można dzwonić. Przy wywołaniu metody call, nasza klasa powinna wywołać na obiekcie Phone metodę connect, po czym w zależności od tego co ona zwróci talk lub disconnect. Zatem po kolei wywołania wyglądają tak:
1. caller.call(Phone phone) Wewnątrz obiektu caller:
2. phone.connect
3. phone.disconnect lub phone.talk
Załóżmy teraz, że piszemy test dla klasy Caller. Nie możemy lub nie chcemy (a często nawet nie powinniśmy) używać w nim obiektu Phone. Może być problem z dostępem do niego (np. testujemy nie mając dostępu do środowiska z podłączonymi telefonami), możemy nie chcieć z niego korzystać (nie chcemy na prawdę gdzieś dzwonić). W takiej sytuacji z pomocą przychodzą nam właśnie mock objects. "Zmockujemy" sobie obiekt klasy Phone i jego będziemy używać.
Wszystkie biblioteki do mocków dadzą sobie radę jeśli Phone będzie interfejsem. Większość da sobie radę także jeśli będzie zwykłą klasą. Do tej większości należy Mockito, które chciałbym dzisiaj opisać. Aby uzyskać sztuczny obiekt Phone, robimy tak:
Na początku potrzebny jest import Mockito. Aby ułatwić sobie życie, zaimportujemy statycznie wszystkie metody tej klasy:
Mock - z angielskiego "udawać", "imitować". W programowaniu "mock object" oznacza obiekt, który udaje inny obiekt. Najlepiej chyba będzie wyjaśnić to na przykładzie.
Załóżmy, że mamy klasę Caller, realizującą połączenia telefoniczne, i klasę Phone, reprezentującą telefon (numer) na który można dzwonić. Przy wywołaniu metody call, nasza klasa powinna wywołać na obiekcie Phone metodę connect, po czym w zależności od tego co ona zwróci talk lub disconnect. Zatem po kolei wywołania wyglądają tak:
1. caller.call(Phone phone) Wewnątrz obiektu caller:
2. phone.connect
3. phone.disconnect lub phone.talk
Załóżmy teraz, że piszemy test dla klasy Caller. Nie możemy lub nie chcemy (a często nawet nie powinniśmy) używać w nim obiektu Phone. Może być problem z dostępem do niego (np. testujemy nie mając dostępu do środowiska z podłączonymi telefonami), możemy nie chcieć z niego korzystać (nie chcemy na prawdę gdzieś dzwonić). W takiej sytuacji z pomocą przychodzą nam właśnie mock objects. "Zmockujemy" sobie obiekt klasy Phone i jego będziemy używać.
Wszystkie biblioteki do mocków dadzą sobie radę jeśli Phone będzie interfejsem. Większość da sobie radę także jeśli będzie zwykłą klasą. Do tej większości należy Mockito, które chciałbym dzisiaj opisać. Aby uzyskać sztuczny obiekt Phone, robimy tak:
Na początku potrzebny jest import Mockito. Aby ułatwić sobie życie, zaimportujemy statycznie wszystkie metody tej klasy:
import static org.mockito.Mockito.*;
Teraz reszta naszego testu:
W ten sposób zweryfikowaliśmy, czy Caller wywołał metodę connect na Phone. Jednak to nie wszystko co chcielibyśmy sprawdzić. Metoda connect zwraca pewien status, i w zależności od niego Caller powinien wywołać jeszcze talk lub disconnect. Jak zaprogramować mocka, żeby zwrócił odpowiednią wartość po wywołaniu metody? Nie jest to nic trudnego:
Zatem cały test wyglądałby tak:
To jednak nie wszystkie możliwości Mockito. W powyższym przykładzie deklarujemy co ma zwrócić Phone jeśli zostanie wywołana na nim metoda connect. Metoda ta nie przyjmuje żadnych parametrów. Co gdyby przyjmowała, i chcielibyśmy żeby mock zwracał różne wartości w zależności od tego z jakim parametrem została wywołana? Załóżmy że Phone ma też metodę say, przyjmującą parametr typu String i zwracającą wartość tego samego typu. Caller wywołuje tę metodę przekazując "Good afternoon" na początku rozmowy, i "Good bye" na końcu. Chcielibyśmy zaprogramować mocka tak, żeby "odpowiadał" tym samym powitaniem i pożegnaniem:
Możemy też używać tzw. argument matcherów:
W ten sposób zwrócimy określoną wartość niezależnie od tego co dostaniemy w parametrze. Oczywiście można też rzucać wyjątki:
DirtyWordMatcher to nasz własny matcher, który zwraca true jeśli napotka niecenzuralne słowa :)
Inną możliwością jest sprawdzanie czy dana metoda została wywołana daną ilość razy:
W ten sposób weryfikujemy czy metoda say() została wywołana 3 razy. Możemy też sprawdzić czy nie została nigdy wywołana, lub sprawdzić czy metody zostały wywołane w odpowiedniej kolejności. Aby dowiedzieć się jak to się robi, odsyłam już do dokumentacji mockito.
Phone phone = mock
(
Phone.
class
)
;
Caller instance = new Caller();
instance.call(phone);
verify(phone).connect();
W ten sposób zweryfikowaliśmy, czy Caller wywołał metodę connect na Phone. Jednak to nie wszystko co chcielibyśmy sprawdzić. Metoda connect zwraca pewien status, i w zależności od niego Caller powinien wywołać jeszcze talk lub disconnect. Jak zaprogramować mocka, żeby zwrócił odpowiednią wartość po wywołaniu metody? Nie jest to nic trudnego:
stub
(phone.connect()).toReturn(Status.BUSY);
Zatem cały test wyglądałby tak:
Phone phone = mock(Phone.class);
Caller instance = new Caller();
stub(phone.connect()).toReturn(Status.BUSY);
instance.call(phone);
verify(phone).connect();
verify(phone).disconnect();
To jednak nie wszystkie możliwości Mockito. W powyższym przykładzie deklarujemy co ma zwrócić Phone jeśli zostanie wywołana na nim metoda connect. Metoda ta nie przyjmuje żadnych parametrów. Co gdyby przyjmowała, i chcielibyśmy żeby mock zwracał różne wartości w zależności od tego z jakim parametrem została wywołana? Załóżmy że Phone ma też metodę say, przyjmującą parametr typu String i zwracającą wartość tego samego typu. Caller wywołuje tę metodę przekazując "Good afternoon" na początku rozmowy, i "Good bye" na końcu. Chcielibyśmy zaprogramować mocka tak, żeby "odpowiadał" tym samym powitaniem i pożegnaniem:
stub
(
phone.say
(
"Good afternoon"
))
.toReturn
(
"Good afternoon"
);
stub(phone.say("Good bye")).toReturn("Good bye");
Możemy też używać tzw. argument matcherów:
stub(phone.say(anyString())).toReturn("You said something but I could not understand");
W ten sposób zwrócimy określoną wartość niezależnie od tego co dostaniemy w parametrze. Oczywiście można też rzucać wyjątki:
stub(phone.say(argThat(new DirtyWordMatcher())).toThrow(new IllegalArgumentException());
DirtyWordMatcher to nasz własny matcher, który zwraca true jeśli napotka niecenzuralne słowa :)
Inną możliwością jest sprawdzanie czy dana metoda została wywołana daną ilość razy:
verify(phone, times(3)).say();
W ten sposób weryfikujemy czy metoda say() została wywołana 3 razy. Możemy też sprawdzić czy nie została nigdy wywołana, lub sprawdzić czy metody zostały wywołane w odpowiedniej kolejności. Aby dowiedzieć się jak to się robi, odsyłam już do dokumentacji mockito.
Friday, July 4, 2008
Upload do repozytorium mavena
Załóżmy, że mamy własne repozytorium mavena (np. nexusa, opisanego przez Leszka tutaj). Czasem jest potrzeba dorzucenia do takiego repozytorium "cudzej" biblioteki. Np. wtedy, gdy potrzebna nam jej nowa, świeża wersja, której jeszcze nie ma w repozytoriach ogólnodostępnych, ale można ją ściągnąć z internetu.
Przypuśćmy, że dołączyć chcemy bibliotekę httpunit w wersji 1.7. Mamy ją w pliku httpunit.jar w katalogu /home/franek/tmp/httpunit.jar. Nasze repozytorium jest pod adresem http://myserver/maven-repo
mvn deploy:deploy-file -DgroupId=httpunit -DartifactId=httpunit -Dversion=1.7 -Dpackaging=jar -Dfile=/home/franek/tmp/httpunit.jar -Durl=http://myserver/maven-repo -DrepositoryId=main
Maven sam wygeneruje plik POM. Jednak zdecydowanie lepiej jest mu go dostarczyć jeśli tylko takim dysponujemy. Inaczej narażamy się na to, że w wygenerowanym POMie może brakować bibliotek od których httpunit zależy. Załóżmy, że POM leży w katalogu /home/franek/tmp/pom.xml. Aby go dodać, do komendy trzeba dodać parametr -DpomFile=/home/franek/tmp/pom.xml. Jeśli dołączamy POMa, nie trzeba podawać parametrów groupId, artifactId, version i packaging - maven odczyta je z POMa.
Jeśli repozytorium wymaga nazwy użytkownika i hasła, trzeba je jeszcze ustawić w ~/.m2/settings.xml
Do biblioteki dorzuconej do repozytorium warto także dołączyć źródła:
mvn deploy:deploy-file -DgroupId=httpunit -DartifactId=httpunit -Dversion=1.7 -Dpackaging=java-source -Dfile=/home/franek/tmp/httpunit-src.jar -Durl=http://myserver/maven-repo -DrepositoryId=main -DgeneratePom=false
Na czerwono oznaczyłem fragmenty, które różnią się w stosunku do komendy służącej do wrzucania jara.
Przypuśćmy, że dołączyć chcemy bibliotekę httpunit w wersji 1.7. Mamy ją w pliku httpunit.jar w katalogu /home/franek/tmp/httpunit.jar. Nasze repozytorium jest pod adresem http://myserver/maven-repo
mvn deploy:deploy-file -DgroupId=httpunit -DartifactId=httpunit -Dversion=1.7 -Dpackaging=jar -Dfile=/home/franek/tmp/httpunit.jar -Durl=http://myserver/maven-repo -DrepositoryId=main
Maven sam wygeneruje plik POM. Jednak zdecydowanie lepiej jest mu go dostarczyć jeśli tylko takim dysponujemy. Inaczej narażamy się na to, że w wygenerowanym POMie może brakować bibliotek od których httpunit zależy. Załóżmy, że POM leży w katalogu /home/franek/tmp/pom.xml. Aby go dodać, do komendy trzeba dodać parametr -DpomFile=/home/franek/tmp/pom.xml. Jeśli dołączamy POMa, nie trzeba podawać parametrów groupId, artifactId, version i packaging - maven odczyta je z POMa.
Jeśli repozytorium wymaga nazwy użytkownika i hasła, trzeba je jeszcze ustawić w ~/.m2/settings.xml
<settings>
...
<servers>
<server>
<id>main</id>
<username>mvn</username>
<password>mvn</password>
</server>
</servers>
...
</settings>
Do biblioteki dorzuconej do repozytorium warto także dołączyć źródła:
mvn deploy:deploy-file -DgroupId=httpunit -DartifactId=httpunit -Dversion=1.7 -Dpackaging=java-source -Dfile=/home/franek/tmp/httpunit-src.jar -Durl=http://myserver/maven-repo -DrepositoryId=main -DgeneratePom=false
Na czerwono oznaczyłem fragmenty, które różnią się w stosunku do komendy służącej do wrzucania jara.
Subscribe to:
Posts (Atom)