czwartek, 13 listopada 2008

Name clash: has the same erasure but does not override it

Pisząc aplikacje wykorzystujące generyki, możecie natrafić na przedziwny błąd. Wyobraźcie sobie interfejs IDao (z pojedyncza metoda updateStubs(List<T>) oraz klasę, która go implementuje AbstractDao. I nagle kompilator krzyczy:

Name clash: The method updateStubs(List) of type AbstractDao<T> has the same erasure as updateStubs(List) of type IDao but does not override it.


Zaraz zaraz, metoda z klasy ma taką samantykę i erasure jak nadrzędna metoda z interfejsu, ale jej nie nadpisuje? O co chodzi?
Spójrzmy na początek definicji interfejsu:


public interface IDao<T extends IStub> {


a następnie na początek defnicji klasy AbstractDao:


public abstract class AbstractDao<T extends IStub> implements IDao {


Co jest nie tak? Otóż wprawna osoba odrazu zauważy (a nieroztropny [czytaj ja] programista przegapi) fakt, iż przy IDao brakuje odwołania do typu generycznego! Poprawnie powyższa definicja powinna wyglądać następująco:


public abstract class AbstractDao<T extends IStub> implements IDao<T> {


Po tej małej zmianie wszystko powino być już w porządku, a kompilator nie krzyczy, że ma metode w podklasie, ale jej nie nadpisze.

poniedziałek, 10 listopada 2008

Java Killers #009

Jakiś czas temu odezwał się do mnie dobry znajomy Paweł Badeński, który uhahany rozpoczął rozmowę na gadu z tekstem "Patrz na to: ..." po czym wkleił kawałek kodu. Kod przerzuciłem na szybko do ulubionego IDE i już wtedy wiedziałem, że te kilka lini ma predyspozycje na kolejny odcinek killersów. Problem pozostawał jednak wciąż, gdyż za diabła nie wiedziałem, czemu kod zachowywał się właśnie tak a nie inaczej. Szybkie przestudiowania Java Language Specification nie dało odpowiedzi, google na szybko też nie były takie cwane, jak zwykle. Dopiero po dłuższej chwili grzebania udało mi się dotrzeć do buga na bugs.sun.com, gdzie po dogłebnej lekturze i ponownym otworzeniu Java Language Specification, wszystko stało się jasne. Jestem ciekaw czy i dla Was dziewiąta odsłona Java Killers będzie taką samą łamigłówką jak była dla mnie. A oto ona:

Mając poniższy kod, jakiego spodziewamy się outputu:


public class Foo
{
public int loo(List<String> a)
{
return 1;
}

public double loo(List<Integer> a)
{
return -1;
}

public static void main(String[] args)
{
List list = new ArrayList<Integer>();
System.out.println("output: " + new Foo().loo(list));
}

}
Odpowiedź poprawna tylko jedna, a kilka do wyboru:


A. Klasa nie skompiluje się, javac poinformuje nas, że "name clash: loo(java.util.List) and loo(java.util.List) have the same signature and erasure"
B. Klasa nie skompiluje się, javac poinformuje nas, że "reference to loo is ambiguous"
C. Skompiluje się, ale wyrzucony zostanie RuntimeException po uruchomieniu programu
D. Program skompiluje się i uruchomi się bez wyjątków i zostanie wypisane na ekranie 'output: -1'
E. Program skompiluje się i uruchomi się bez wyjątków i zostanie wypisane na ekranie 'output: 1'



I uwaga uwaga, poprawna odpowiedź to B!

Osoby, które przekonane były, że oby dwie metody loo miały taką samą sygnaturę i przez to kod się nie kompilował, nie martwcie się, nie byliście jedyni, którzy zaznaczyli odpowiedź A. Otóż okazuje się, że jednak sygnatury obu metod się różnią i występuje najprostszy w świecie method overloading. Później w czasie wywołania metody loo w ciele metody main, następuje konsternacja, gdyż kompilator widzi zmienną niegenneryczną List list i nie ma pojęcia do której metody loo ma się odwołać, stąd jego krzyk
"reference to loo is ambiguous".
Ok, ktoś się spyta, jak to metody loo mają taką samą sygnaturę? Patrząc na Java Language Specification punkt 8.4.9, widzimy, że:

* obie metody mają taką samą nazwę
* obie metody mają taką samą liczbę formalnych parametrów oraz parametrów typów prostych
* ALE typy tych parametrów się różnią, List<String> to co innego niż List<Integer>! Mimo, że przed samą kompilacją wszystkie generyki zostają zrzucone i nie są brane pod uwagę podczas kompilacji (pisałem o tym w poprzednich killersach) o tyle są uwzględniane podczas operacji zwanej "type erasure" - dlatego właśnie List<String> to co innego niż List<Integer>.

No i to tyle na dziś. Kto znał poprawną odpowiedź?

PS. Wspomniana strona do której dotarłem (i musiałem się nieźle nagooglać) znajduje się tutaj: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6182950

środa, 5 listopada 2008

Maven javaee-api-5

Jeśli dane jest Wam pisać aplikacje JEE5 z użyciem mavena, dość paradoksalnie możecie się spotkać z brakiem w dostępnych repozytoriach pliku javaee-api-5.jar (definiującego API standardu JEE5). Jeśli więc kiedykolwiek dostaniecie informacje od mavena, że nie może za żadne skarby znaleźć API JEE5, polecam zajrzeć tutaj:

https://maven-repository.dev.java.net/nonav/repository/javaee/jars/

W repozytorium java.net jar z API standardu leży sobie grzecznie, czekając, aż go ktoś ściągnie.

wtorek, 4 listopada 2008

Zmiana portu JBoss Application Server z 8080 na inny

JBoss Application Server uruchamia się standardowo na porcie 8080. W odróżnieniu od Glassfisha nie ma admina webowego (co jest zastanawiające czemu) i żeby cokolwiek zmienić w konfiguracji, trzeba poedytować pliki konfigów.

W tym poście zakładam, że JBoss zainstalowany jest w katalogu /opt/jboss/. Aby zmienić port HTTP z 8080 na jakiś inny należy dostać się do pliku server.xml zlokalizownego w /opt/jboss/server/default/deploy/jboss-web.deployer/ i wyedtować go. Odnajdujem poniższy wpis i w miejscu XXX wpisujemy intersujący nas port (na przykład 80):


<Connector port="XXX" address="${jboss.bind.address}"
maxThreads="250" maxHttpHeaderSize="8192"
emptySessionPath="true" protocol="HTTP/1.1"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" />

poniedziałek, 3 listopada 2008

Oracle Apex widoczny poza localhost'em

Ostatnio pisałem jak zainstalować Oracla na Ubuntu, dziś pora przyjrzeć się aplikacji webowej Apex (pozwalająca zarządzać bazą danych), która standardowo instaluje się wraz z Oraclem i dostępna jest pod adresem http://localhost:8080/apex. Wszystko działa fajnie, do czasu gdy chcielibyśmy dostać się do aplikacji przez inny adres niż http://localhost, czyli zdalnie. Poniżej krótka instrukcja jak to zrobić:

1. zainstaluj sqlplus jeśli (jeszcze nie masz):

apt-get install sqlplus

2. ustaw ORACLE_HOME (jesli jeszcze tego nie zrobiles):

ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server

3. zaloguj się do sqlplusa jako user SYSTEM

sqlplus system

4. gdy pojawi się sql prompt (ktoś zna polski odpowiednik słówka prompt?) wpisz:

SQL> EXEC DBMS_XDB.SETLISTENERLOCALACCESS(FALSE);


Powinno zacząć działać.

niedziela, 2 listopada 2008

Where is everybody?


"Where is everybody???" No właśnie, dobre pytanie. Blog trochę podumarł i przycichł. Czemu? Otóż zaczynamy z kolegami mały skromny start-up (o którym może kiedyś, jak już coś będzie wydane na świat) i przez ostatnie dwa miesiące poświęciłem się mu bezgranicznie. Teraz troszkę się już uspokoiło i ponownie mogę wrócić do zajęcia, które sprawia mi też sporo radości - blogowania.

Do zobaczenia niebawem, trochę się nazbierało dziwnych sytuacji, o których warto by tu napisać.

czwartek, 28 sierpnia 2008

Wyszedł pierwszy numer JAVA exPress!

Wyszedł pierwszy numer JAVA exPress, jakieś kilka godzin temu, a w polskiej blogosferze wciąż cicho (nawet odziwo na blogu głównego pomysłodawcy), więc pozwalam sobie poinformować o wydaniu pierwszego (i mam nadzieje nie ostatniego) numeru JAVA exPress, który w postaci pdf'a można za darmo sciągnąć stąd.
Coż można powiedzieć po wstępnym przejrzeniu gazetki? Widać, że zarówno pomysłodawca (Grzegorz Duda) jak i autorzy (Bartek Kuczyński, Jakub Jurkiewicz, Mariusz Sieraczkiewicz, Jackiw Pospychała oraz Marek Kliś) bardzo mocno się postarali, aby pierwszy numer wypadł ciekawie. Jeśli kolejne numery utrzymają formę, zapowiada się bardzo ciekawy zin w polskim światku javowym.
Boli tylko troszkę, że nie można takiej gazetki nigdzie kupić w formię drukowanej. Piękno przecież takich artykułów polega na tym, że można je sobie spokojnie poczytać w pociągu, samolocie czy też w domu leżąc wygodnie na kanapie. Niestesty na papierową wersję JAVA exPress trzeba będzie nam jeszcze sporo poczekać i narazie zadowolić się będzie trzeba SDJ (gdzie poziom artykułów zwykle bliżej dna niż powierzchni).

środa, 27 sierpnia 2008

Instalacja Oracle na Ubuntu

For English press http://paulszulc.wordpress.com/2009/05/25/installing-oracle-on-ubuntu/

Oracle to baza danych przez duże B i duże D. Wystarczy poczytać sobię trochę o architekturze Oracla, aby przekonać się czemu jest to jedna z najczęściej wykorzystywanych baz w biznesie. Dotychczas (na studiach :) ) wiedza o bazach danych była mi na tyle potrzebna, co założyć bazę, stworzyć strukture (tabele, widoki, indexy) i na końcu zmapować to wszystko w jakis ORM (Hibernate czy inny TopLink). Ponieważ w pracy wymagają ode mnie troszkę większej wiedzy na temat Oracla i baz danych w ogólności, postanowiłem po godzinach przyjrzeć się tej bazie u siebie na mojej lokalnej maszynie.
Aby to uczynić należało wpierw bazę tą zainstalować, co okazało się względnie prostym zadaniem na systemie Ubuntu (obecnie w wersji 8.04). Kolejne kroki w wersji skróconej podaje poniżej:

1. Dodać nowe repozytorium

1.1 Do listy należy dodać deb http://oss.oracle.com/debian unstable main non-free. Można to zrobić wyklikując System->SoftwareSources->Third-Party Software (jak na poniższym obrazku) lub dodać wpis ręcznie do /etc/apt/sources.list - jak kto woli.

1.2 Dodać klucz publiczny, najprościej w konsoli wpisując

wget http://oss.oracle.com/el4/RPM-GPG-KEY-oracle  -O- | sudo apt-key add -
ale można oczywiście również wyklikać przez System->SoftwareSources->Authentication

2. Uruchomić instalację Oracla

Wystarczy teraz już tylko standardowe:

sudo apt-get install oracle-xe

i po wykonanej instalacji uruchomić program konfigurujący:

sudo /etc/init.d/oracle-xe configure

gdzie odpowiada się na cztery proste pytania: o port dla Oracle Application Express (defaultowo 8080), port do połączeń z bazą danych (defaultowo 1521), hasło dla administratora (użytkownika SYSTEM) oraz określa się czy baza ma uruchamiać się wraz z uruchamianiem się systemu.

I to tyle! Mamy działającą instancję Oracla. Możemy zacząć zabawę z systemem uruchamiając SQL*Plus'a lub application express (http://127.0.0.1:8080/apex).

poniedziałek, 25 sierpnia 2008

java.util.Date vs java.sql.Date

Natknąłem się na ciekawe rozwiązanie architektoniczne w JDBC: obiekt java.sql.Date. Jest to obiekt, który dziedziczy bezpośrednio po java.util.Date, pozwalający nam mapować w naszych beanch pola typu Date z naszych baz danych.
Czy wszystko jest takie piekne? Nie do końca, gdyż w naszych beanach zwykle trzymamy obiekty typu java.util.Date.
Wyboraźmy sobie prosty przykład:

Mamy klase User w postaci

public class User
{
protected String userName;
protected Date birthDate; // tutaj java.util.Date czyli standard

//odpowiednie gettery i settery
}
Posiadamy również odpowiadającą jej tabelę w bazie danych:

CREATE TABLE Users (
userName VARCHAR2(150),
birthDate DATE );

Sytuacja jak najbardziej standardowa. Teraz jeśli wyciągnięte dane z bazy (przy pomocy ResultSeta) dodamy do naszego beana:

User u = new User();
u.setUserName(rSet.getString(1));
u.setBirthDate(rSet.getDate(2));

nic się nie stanie. W prawdzie metoda User.setBirthDate oczekuję java.util.Date, a ResultSet.getDate zwraca java.sql.Date, to jednak wszystko jest w porządku, gdyż java.sql.Date dziedziczy po java.util.Date. Jaja zaczynają się, gdy informacje chcemy ponownie przekazać do bazy danych.
Teraz gdy updateujemy jakies informacje przy pomocy jakies procedury skladowanej czy poprzez wywolanie SQL'owego UPDATE, zawsze ale to zawsze poprosza nas o Date w postaci java.sql.Date. Czyli kod
statement.setDate(2,user.getBirthDate());
sie po prostu nie skompiluje. Rzutowanie nic tu nie da, gdyż przykładowo poniższy kod:
user.setBirthDate(new Date());
statement.setDate(2,(java.sql.Date)user.getBirthDate()); // tu exception
się poprostu wywali przy pierwszym wywołaniu. Trzeba utworzyć explicite obiekt java.sql.Date, ale żeby nie było za prosto, nie ma żadnego konstruktora typu public Date(java.util.Date utilDate). Nie, przecież to by było zbyt proste. Zamiast tego musimy zrobic new java.sql.Date(utilDate.getTime()). Konstruktor java.sql.Date przyjmuje longa jako liczbę milisekund od roku 1970, a metoda getTime z java.util.Date taka informacje zwraca. Wszystko działa okej, ale czy na pewno to jest dobre rozwiązanie? Szczerze mówiąc, ja nie wiem. Skoro sami ludzie z Sun Microsystems mówią, że java.sql.Date to tylko "thin wrapper", to po co w ogóle został wprowadzony? Czy istnieje jakiś lepszy (w sensie nie polegający na roku 1970) sposób na konwertowanie dat z java.util na java.sql? Googlałem pobieżnie, nie znalazłem. Może ktoś zna?

środa, 20 sierpnia 2008

Po przerwie wakacyjnej

Minał okres wakacji i laby. Po obronie pracy magisterskiej nie mogłem przez jakiś czas dojść do siebie - w czym z pewnością usilnie pomagał mi alkohol ;). Potem wyjechałem ze znajomymi na długo wyczekiwany rejs po Mazurach (długi, bo aż ponad trzy tygodniowy). No a teraz..., teraz znów wróciłem do świata żywych i czas odświeżyć wiele spraw, między innymi tego bloga :)

Rozpocząłem prace w ifirma.pl, po moim długim maratonie po firmach IT we Wrocławiu. Zatrudnony jestem jako programista Java oraz PL/SQL, zatem spodziewać się można, że sporo zagadnień na blogu związanych będzie teraz z Oraclem. Obecnie w pracy rozgryzam sprawy związane z webserwisami, więc zapewne jakieś relacje z moich walk z Axis'em (implementacja z Apache'a) się tu pojawią.

Generalnie każdego dnia czegoś nowego się uczę, nierzadko na własnych błedach. Chciałbym aby ten blog był też zapiskiem z tych moich walk i pojedynków z technologiami, tak żeby w przyszłości mógł ktoś kiedyś z tego skorzystać.

Aby zatem nie przedłużać, w wielkim skrócie: I am back :)

wtorek, 24 czerwca 2008

Tools Dig: LiquiBase

Wczoraj marudziłem na brak railsowych migracji w popularnych frameworkach javowych, a już dziś dzięki uprzejmości Pawła Sto (p100 :) ), poznałem bardzo ciekawe narzędzie jakim jest LiquiBase. Narzędzie to posiada większość elementów z railsowych migracji oraz jest nawet śmiem twierdzić nieco lepsze od nich (odczuć mogą to zwłaszcza teamy developerskie, gdzie jest więcej niż 4 programistów i jedno repozytorium kodu :) ).

Dlatego wszem i wobec zachęcam wszystkich do zapoznania się z toolem (http://www.liquibase.org). Warto zwłaszcza zerknąć na screencasty: krótkim (6min) lub dłuższym, obszerniejszym (30min). Omawiają starszą wersję narzędzia, ale dają ogólny taki overviewu, warto obejrzeć.

Niniejszym rozpoczynam też nową mini-serię na moim blogu: Tools Dig . Często napotykam na ciekawe narzędzia w sieci. Jak zwrócił mi uwagę mój kolega, warto się takim znaleziskami dzielić z szerszą publiką, a do tego idealnie nadaje się ten blog. Co niniejszym czynię.

poniedziałek, 23 czerwca 2008

Migracje bazodanowe: czego w Javie nie znajdziemy?

Czytam sobie sporo o Railsach ostatnio. Na tyle mi się framework spodobał (i język Ruby też), że chwilowo nawet myślałem o całkowitej migracji Java -> Ruby & Rails. Ponieważ jednak firm RoR'owych u nas we Wrocławiu jak na lekarstwo (w sumie tylko jedna, pracowników serdecznie pozdrawiam :) ), to ostatecznie pomysł ten zarzuciłem. Nie zmienia to faktu, że czytanie książek i blogów o Railsach sporo mi dało. Inaczej teraz trochę patrze na programowanie, jest też kilka spraw, które w frameworkach Javowych bardzo mi od tego czasu brakuje.
Jednym z takich elementów są migracje (ang. migrations), potężne narządzie frameworka Ruby on Rails.
W Javie pisząc aplikacje wykorzystujące bazę danych przyzwyczailiśmy się już do frameworków tzw. mapujących. To znaczy takich, w których model biznesowy jest mocno zintegrowany z bazą danych. Boli nas trochę fakt, że gdy tworzymy aplikacje z wykorzystaniem jakiegoś narzędzia ORM'owego, musimy wyspecyfikować:

- pole w modelu
- getter
- setter
- mapowanie w kierunku a->b
- mapowanie w kierunku b->a
- definicje kolumny

Oczywiście nie można dramatyzować: model (a wiec odpowiednie fieldy, gettery, settery) pomoże nam stworzyć nasze ulubione IDE, a najnowsze frameworki (z wykorzystaniem adnotacji) umiejscawiają nam definicje bazodanowe w samym modelu. Wszystko robi się niby DRY (Don't Repeat Yourself), wszystko wydaje się być w jednym miejscu i wszelkie zmiany w projekcie można wprowadzać bezboleśnie. Zapominamy tutaj o jednej ważnej sprawie - danych w bazie danych.

Możemy wyobrazić sobie, że po miesiącu czy dwóch naszego developingu, powstaje aplikacja/prototyp, która zostaje wdrożona u klienta. Schemat bazy danych jest oczywiście generowany z naszego modelu na bazie produkcyjnej u klienta. Czysta, nowa, świeżutka baza wraz z aplikacją zostaje uruchomiona. My zabieramy się za kolejną iterację, a klient wdraża się w jej pierwszą wersję. W między czasie dodajemy nowe kolumny, zmieniamy nazwy tabel, łączymy pewne rzeczy w jedną całość - jesteśmy bardzo agile - nie boimy sie zmian. Przychodzimy do klienta, chcemy mu wygenerować nowa bazę a on nam mówi "Hola hola, ja tu już mam pewne dane, nie chce ich tracić". Po chwili namysłu dochodzimy do wniosku, że możemy wygenerować plik SQL, w którym umieścimy wszystkie zmiany w nowej bazie danych względem tej starej (znany wszystkim DDL, czyli alter table i takie tam) i tak gotowy skrypt uruchomimy po stronie bazy produkcyjnej.

Ok, rozwiązanie niby dobre. Spotkałem się z nim w praktyce, pliki takie nazywane były "patchami" (moim zdaniem nazwa dobra) i tworzone były dla każdej kolejnej iteracji przygotowywanej aplikacji. Trzymane wraz z kodem w repozytorium nie miały prawa zaginąć. Teraz tworząc pole String nickname w modelu User musiałem dodatkowo w patchu patch_078.sql umieszczać wpis "alter table add column nickname varchar". W momencie integracji wersji deweloperskiej z produkcyjną, po wprowadzeniu zmian w kodzie uruchamiany zostawał patch i schemat bazy był aktualny z modelem. Wszystko niby fajnie, ale właśnie tu zaczynają się schody. Po pierwsze przestaje mieć pojedyńczej informacji (w tym przypadku informacji o schemacie bazodanowym) w jednym miejscu - co szybko prowadzić może do pomyłek. Druga sprawa to brak powiązania tych zmian z kodem na repozytorium. Wprowadzę zmiany w kodzie, commitne, mnie trochę czasu, update'uje patch na repozytorium. Mijają cztery dni i okazuje się, że muszę cofnąć zmiany w moim modelu. Muszę PAMIĘTAĆ, że odpowiednie zmiany powinny być update'owane w patchu (rollback na repozytorium mi tego nie zapewni). Ból, ból, ból i zgrzytanie zębów.

O wiele ciekawiej rozwiązane jest to w Railsach. Patche (tutaj zwane migracjami) tworzone są za nas automatycznie. Taki patch zawiera nie tylko metodę updatującą schemat bazy, ale również wycofującą te zmiany. W ogólności każda migracja to klasa Ruby'ego implementująca dwie metody up oraz down. Metoda up wprowadza zmiany w schemacie bazy danych, metoda down je wycofuje. Przez co uzyskujemy patch działający w dwie strony. Zestaw operacji jakie możemy wykonać jest dowolny, możemy wstawiać dane, zmieniać strukturę schematu (z użyciem predefiniowanych metod podobnych do tych które znamy z DDL, czyli create/drop table, add/remove column i tak dalej). Każda taka migracja ma przypisany swój numer porządkowy, a sam schemat trzyma informacje o swojej wersji. Jeśli zatem przykładowo mamy schemat bazy danych w wersji 004, a w czasie developingu pojawiły się dwie nowe migracje 005 oraz 006, to wywolanie komendy rake migrate spowoduje, że framework wywoła w kolejności metody up, najpierw z migracji 005, a następnie z migracji 006,a na końcu zmieni wersję schematu na 006. Jeśli kiedykolwiek trzeba będzie sie cofnąć w zmianach schematu, na przykład do wersji 004, wykonane zostaną kolejno metody down w migracji 006, a poźniej w 005. Piękna sprawa!

Klasy migracji są tworzone dla modeli automatycznie, więc nie ma obawy, że do repozytorium nie zostaną przekazane razem. Wtedy cofnięcie się w ramach repozytorium do kodu z przed na przykład kilku dni nie stanowi problemu.

O migracjach w Rails można by mówić wiele, fakt faktem brakuje mi czegoś takiego w frameworkach Javowych. Słyszałem, że jakieś OMR'y generują patche dla konkretnej bazy danych w oparciu o zmiany jakie zaszły w modelu. Jakie? Nie wiem, może wy mi powiecie, jestem bardzo ciekaw. Wtedy byłoby już fajnie, chociaż wciąż nie pozostawalibyśmy DRY, ale przynajmniej były to już jakiś krok w dobrym kierunku.

Orginał tego postu wyglądał ciutkę inaczej od tego co tu przeczytałeś, ale durny siersciuch (kot) gonił muchę po mieszkaniu, przebiegł się po klawiaturze, skasował sporą część zaznaczonego tekstu, a blogspot automatycznie po kilku sekundach zapisał zmiany. 40% tekstu jest odtworzone, a więc podatne na błędy i gorsze jakościowo - nie cierpie pisać czegoś dwa razy.

Z kota zrobie jutro gyros.


update:
jeśli ktoś zna jakiś framework, który rzeczywiście na podstawie schematu bazy i modelu, jest w stanie wygenerować skrypt sql ze zmianami jakie należy wprowadzić w bazie, niech da mi prosze znać na maila lub w komentarzach. W JPA szukałem i się nie doszukałem.

sobota, 21 czerwca 2008

Świat kawy

Kawa, źródło życia chyba większości programistów. Chociaż staram się ograniczać, bo by mój żołądek nie wytrzymał, to jednak poranka przed kompem nie wyobrażam sobie bez kubka gorącej kawy z mlekiem.

Tym wstępem nie zaczynam jakiegoś referatu, czy innej rozprawki. Nie bójcie się o to, przynudzać nie będę. W zamian, będę się chwalił. A dokładnie będę się chwalił moją narzeczoną, która zaliczając 4 roku studiów na Architekturze Wnętrz popełniła coś takiego:


Chociaż ona uważa, że nie jest to jej najlepsza praca i miewała ciekawsze, to jednak dla mnie to jest mistrzostwo (czy jak mawiała moja polonistka miczostwo). Do takiego sklepu na filiżankę kawy chętnie bym przychodził. Jest to dla mnie zupełnie inna jakość, coś znacznie ciekawszego od tego, do czego przyzwyczaiły nas centra handlowe.

Może przesadzam, w końcu wszystko co wyjdzie z łap Magdy wydaje mi się zajebiste, ale osobiście uważam, że ten projekt dał radę.

Java Killers #008

Witam po dłuższej przerwie w kolejnej odsłonie cyklu Java Killers. Standardowo bez dłuższych wstępów, przejdziemy od razu do kodu, by główkować, dziwić się i przeklinać.

Oto on:


package javakillers.part008;

/**
* @author pawel zainspirowany koziolekweb
*/
public class Main
{
public static void main( String[] args )
{
foo(null);
foo((Object)null);
}

public static void foo(Object object )
{
System.out.print("object ");
}
public static void foo(Object ... objects )
{
System.out.print("objects ");
}

}


Pytanie, jaki będzie wynik uruchomienia powyższego programu, pozostaje wciąż otwarte. Odpowiedzi do wyboru (do koloru):

A) program się nie skompiluje
B) program się skompiluje, ale wyrzuci java.lang.ClassCastException
C) program się skompiluje, ale wyrzuci java.lang.NullPointerException
D) program się skompiluje, uruchomi i wyświetli "object object"
E) program się skompiluje, uruchomi i wyświetli "objects objects"
F) program się skompiluje, uruchomi i wyświetli "objects object"
G) program się skompiluje, uruchomi i wyświetli "object objects"

Zanim przeczytasz rozwiązanie, spróbuj pomyśleć nad odpowiedzią.


Prawidłowa odpowiedź to F) program się skompiluje, uruchomi i wyświetli "objects object". Czemu? Żeby się dowiedzieć, musicie odwiedzić blog koziołka, bo właśnie tam przeczytałem post, który zainspirował mnie, do dzisiejszego odcinka cyklu Java Killers. Polecam!

piątek, 20 czerwca 2008

Paradoks czasowo-przestrzenny

No i jeszcze z ciekawostek dzisiejszego dnia: dostałem informację od recenzenta, że praca magisterska jest już oceniona, po czym godzinę później dowiedziałem się od kolegi, że mój promotor mnie szuka,ponieważ moja praca magisterska jest jeszcze nie skończona...

Hmm....

Nie wiem do końca jak, czemu i w jaki sposób, ale jest jak jest. Mam ocenioną pracę, która podobno jeszcze nie jest skończona :) W poniedziałek czeka mnie wycieczka (hurra...) na Politechnikę, żeby sprawę wyjaśnić.

Ps. Dostałem 5.0 z recenzji :)

Rozmowy rekrutacyjne - esencja cz. 2

Kolejna ciekawą sprawą jest fakt, że na rozmowach rekrutacyjnych nie za wiele osób sprawdza wiedzę praktyczną. Zwykle osoba rekrutująca pyta się o poszczególne etapy kariery wypisane w CV, zadając pytania typu "Co było największym osiągnięciem w firmie X", "Jakie były Twoje zadania, gdy pracowałeś w Y". Udzielając odpowiedzi na nie pokazujemy rekrutującemu czy po pierwsze na prawdę tam pracowaliśmy (można być zatrudnionym jako programista, a w praktyce podawać kawę przez większość dnia). Poza tym sprawdzane jest jaki był nasz zakres kompetencji, jakie były stawiane przed nami problemy i co takiego tak na prawdę wnieśliśmy do firmy. Wszystko fajnie, z tym, że uważam, że dobry ściemniacz i lawirant, poradziłby sobie bez problemu w opowiadaniu czego on nie zrobił i jak pięknie to nie działało. Każdy może przecież upiększać i koloryzować swoje osiągnięcia. Dlatego dziwi mnie fakt, że na rozmowach o pracę nie proszono mnie o pisanie choćby kawałka kodu. A to przecież kod tak na prawdę świadczy o programiście.
Często pojawiają się pytania z dziedziny informatyki, Javy, wzorców projektowych. I to już jest rzeczywiście fajnie, ale uważam, że to wciąż za mało i choćby kawałek kodu do napisania mówił by o wiele więcej, niż tysiące sztampowych, powtarzających się w kółko pytań. W idealnym układzie powinno się prosić o napisanie choćby kawałka kodu, lecz nie zostawiać programisty samemu sobie, a patrzeć jak podchodzi do problemu. Ocenić logiczne myślenie, (nie)szablonowość, radzenie sobie z problemami. Żywe zadanie, żywy problem - to powinno być esencją rozmowy rekrutacyjnej. Chyba tylko w czasie jednej rozmowy o pracę poproszono mnie o napisanie kawałka kodu.

Można zadać sobie pytanie - a co mnie to obchodzi, przecież to nie ja rekrutuje. Niby tak i też nie do końca. Zakładając, że idę do pracy do takiej firmy, gdzie rekrutacja była poprowadzona po łebkach, mogę się spodziewać, że do mojego zespołu trafią: lawiranci, lenie, ściemniacze, cieniasy (lub hybrydy powyższych). O atmosferze pracy świadczy team! Chce pracować z ludźmi fajnymi, ale również osobami, które coś ze sobą wnoszą, kompetentnymi.
Oczywiście jest okres próbny, w praniu wychodzi, kto tak naprawdę się nadaję i każde kłamstewko wypowiedziane w czasie rozmowy o pracę wyjdzie na jaw. Dziwi mnie tylko fakt, że pracodawcom nie przeszkadza taki tryb. Osobiście uważam, że rekrutacja na stanowisko programisty powinna sprawdzać wiedzę kandydata rzetelnie, dokładnie i do bólu. Przynajmniej na to byłem przygotowany, a w kilku firmach nawet nie słyszałem pytań sprawdzających wiedzę, pracodawca brał wszystko na słowo. Dziwi mnie to, ale może rzeczywiście nie mnie to osądzać.

Oczywiście nie powinienem też dramatyzować. Kilka firm przeprowadziło rzetelne rozmowy rekrutacyjne i jeśli kiedykolwiek miałbym usiąść po drugiej stronie stołka, być osobą zadającą pytania, na pewno korzystałbym z doświadczenia z tamtych spotkań, które uważam za naprawdę udane.

czwartek, 19 czerwca 2008

Rozmowy rekrutacyjne - esencja cz. 1

Jestem obecnie w trakcie poszukiwania pracy. Jakieś 10 dni temu porozsyłałem moje CV po różnych firmach IT we Wrocławiu, a później po krótkim okresie oczekiwania zostałem zapraszany na kolejne rozmowy o pracę. Odwiedziłem kilka(-naście) większych i mniejszych firm IT i z wnioskami z tych spotkań chciałbym się z Wami podzielić. Jeśli szukacie lub planujecie szukać pracy w najbliższym czasie potraktujcie ten wpis jako pewnego typu przewodnik po procesie rekrutacji.
Zacznę na początek od pytań, które pojawiają się zawsze lub prawie zawsze.

"Kim jest Paweł Szulc?"

To pytanie rozwala i niszczy. Pojawia się na samym początku rozmowy rekrutacyjnej i wydaje mi się, że zadawane jest po to, aby już na samym początku zbić nas z tropu. No bo słyszycie takie pytanie i co w ogóle wtedy myślicie? Co to jest za pytanie, jaki jest jego sens? Czy mam teraz w 15 zdaniach opisać swoją osobowość, charakter, życiorys a na końcu odnieść się do tego wszystkiego w pięknym podsumowaniu? Czy może pytają się kim jest Paweł Szulc jako programista, co umie, co potrafi, gdzie zdobył szlify i doświadczenie. Moją pierwszą reakcją było zadanie sobie samemu pytania, jakiej odpowiedzi oni oczekują. Ponieważ nie byłem sobie w stanie na to pytanie sam odpowiedzieć, spytałem się ich: w jakim sensie, w jakim kontekście zadane jest to pytanie. Odpowiedź powala na kolana: "Dowolnym".
Musicie sobie zdać sprawę, że jest to pytanie na krasomówczą pułapkę. Wydaje mi się, że to pytanie zadawane jest po to, żeby tak naprawdę usłyszeć co ciekawego opowie nam ta osoba o sobie. Nie chcą wiedzieć, kim jest Paweł Szulc. W 15 zdań nikt nie jest w stanie siebie opisać i pracodawcy dobrze o tym wiedzą. To pytanie z samej natury ma być enigmatyczne, ma lekko zagiąć, zdziwić. Chodzi o sprawdzenie jak osoba poradzi sobie w takiej sytuacji, czy umie coś o sobie powiedzieć i jeśli tak to w jaki sposób. Ponieważ usłyszałem to pytanie na 90% rozmowach rekrutacyjnych, szanse są, że i wy je usłyszycie. Warto być tego świadomym i mieć przynajmniej szkielet jakieś gadki gotowy na poczekaniu.

"Czemu wolałby Pan pracować w mniejszej firmie?"

Na początek, żeby coś sobie wyjaśnić. Nie aplikowałem mojego CV do żadnej z firm typu Siemiens, Comarch czy innych większych korporacji. Nie ma w tym jakieś konkretnej przyczyny. Po prostu nie bawi mnie praca w firmie, gdzie struktura pracy nakazuje raportowanie do czterech rożnych szefów, gdzie przychodzę pracować jako programista Java kończę jako programista C++. Słyszałem różne historie i jakoś się zraziłem. W życiu zawodowym otarłem się o mniejsze firmy (w sensie mniej niż 100 osób) i z każdą doświadczenie mam jak najbardziej pozytywne. Ja WIEM, że 3/4 studentów po informatyce rzuca się na większe firmy. Jestem tego świadomy. Osobiście się temu dziwie, bo chyba jeśli w takich firmach jest taka rotacja pracowników, że cały czas kogoś nowego przyjmują, bałbym się trochę składać tam papiery.
No ale mam świadomość, że tak jest. Pracodawcy mniejszych firm jak widać też mają. Praktycznie na każdej rozmowie, na której byłem pytano się mnie: "A czemu nie korporacja?". Po trzeciej takiej rozmowie miałem już gotową gadkę i byłem na to pytanie przygotowany, ale z początku w sumie było jedno z tych pytań, które mnie dziwiło, nie dlatego, że nie wiedziałem jak na nie odpowiedzieć, a dlatego, iż dziwiłem się, że w ogóle było zadawane. Dla mnie naturalnym był fakt, że najpierw szukam zatrudnienia przede wszystkim w mniejszych firmach IT. Jak widać nie dla wszystkich jest to naturalne.

"Ile chciałby Pan zarabiać?"

Nie powinno nikogo dziwić, że pytanie to będzie zadane. Osobiście uważam, że trzeba przed rozmową rekrutacyjną dokładnie wiedzieć, ile się chce zarabiać w danej firmie i kwotę to podać natychmiast, po zadaniu pytania. Na jednym wydechu, bez zająknięcia. Wtedy pracodawca widzi, że sprawę mamy przemyślaną, wiemy dokładnie czego chcemy. To pytanie nigdy nie jest negocjacją. Pracodawca odpowie "aha" lub "yhmmm", zapisze sobie kwotę na papierze i o tym czy mu kwota ta pasowała dowiemy się dopiero, po kilku dniach od rozmowy. W całym moim życiu miałem dwa wyjątki od tej reguły: raz osoba rekrutująca spytała się czy nie da się mniej, innym razem rekrutująca osoba powiedziała, że im to odpowiada i czy się decyduje na pracę (tak tak, miałem się określić w dniu rozmowy rekrutacyjnej - rekord świata moim skromnym zdaniem ;) ).
Dlatego powtarzam, na to pytanie trzeba być przygotowanym. Osobną sprawą jest kwestia ile powiedzieć, że chce się zarabiać. To wszystko zależy. Jak już pisałem jakiś czas temu, w Polsce nie obowiązują zachodnie standardy podawania widełek płacowych. Szkoda, naprawdę, ale takie są realia. Sprawę pogorsza fakt, że na takich serwisach jak ocenpracodawce.pl, zwykle piszą albo frustraci, których wywalono z roboty, albo sami pracodawcy, żeby podnieść sobie rankingi. Nie istnieje nic takiego jak średnia dolnośląska czy średnia wrocławska. U jednego pracodawcy X złotych może zarobić już początkujący programista, u innego na start można otrzymać co najwyżej 2/3 tej sumy. Osobiście wydaje mi się, że należy przekalkulować ile się wie i jak się samego siebie ocenia, następnie zrobić research wśród znajomych i kolegów ile zarabiają i odnosząc się do ich wiedzy i umiejętności odnaleźć, gdzieś tam swoją średnią.

Najważniejsze jest jednak, żeby się CENIĆ. Warto to powtórzyć dwa razy: najważniejsze, żeby się cenić. Znam przypadki kolegi, który pracował w firmie IT we Wrocławiu za 1600zl na rękę. Przepraszam, ale w obecnej dobie to się robi po prostu śmieszne. Albo przykład, o którym już kiedyś pisałem, chłopaka, który przez 3 miesiące pracował za darmo na "praktykach" mimo, że wykonywał robotę taką jak wszyscy inni w firmie. Pracodawcy widząc osobę, która aż się prosi, żeby ją wykorzystać, zrobią to, bez wahania. Nigdy, prze nigdy, nie bądźcie takimi osobami! Ceńcie się za swoją wiedzę i doświadczenie. Dla dobra swojego i naszego ogółem.

ciąg dalszy nastąpi....

poniedziałek, 16 czerwca 2008

Porsche nie w najbliższym czasie...

W najbliższym czasie na Porsche raczej mnie nie będzie stać. Wydawać się by się mogło oczywistość - mam 24 lata, na Porsche przyjdzie jeszcze czas... jeśli w ogóle.

Ale ja miałem plan!! Piękny, genialny, plan. Oczywiście serwis (praktycznie nie)społecznościowy, no bo cóż innego mógłbym wymyślić? Jestem informatykiem, od czasów naszej-klasy, każdy z mojej branży pluje sobie w brodę - "przecież ja to mogłem zrobić". Ale ten pomysł był naprawdę świeży, oryginalny. Czegoś takiego na świecie nie było.

No i pielęgnowałem tą ideę, liczyłem dni do końca pracy magisterskiej. Pomysł powstał rok temu, a ja spokojnie sobie czekałem. Plan był prosty: skończyć studia, a następnie zabrać się za to z kopyta i po dwóch latach jeździć nowiutkim Porsche :)

Co się okazało? Pracę oddałem do recenzji 8 czerwca, 2 tygodnie wcześniej powstał portal, w założeniach identyczny do mojego pomysłu. Po prostu taka kopia jeden do jednego. Grrrrr.... jestem zły, wściekły i wkurzony. Jak jeszcze im to wyjdzie, serwis wypali, zdobędą sławę, kobiety i pieniądze, to się chyba po prostu potnę ;).

Rada dla wszystkich: uczcie się na moich błędach. Zwlekanie nie popłaca. Jak coś Was natchnie, bierzcie się od razu do roboty. W innym wypadku będziecie potem tak jak ja, przez dwa dni gryźć klawiaturę, a trzeciego dnia wylewać swoje frustracje na blogu.

niedziela, 1 czerwca 2008

Rails 2.1

Jeszcze niedawno pisałem o problemie jaki stawia przed młodym adeptem Railsów wybór wersji (1.2.6 vs 2.0.2), a już zdążyła wyjść kolejna wersja RoR opatrzona numerkiem 2.1. Z taką prędkością z jaką rozwija się się framework (półroku kolejne wydanie) trudno będzie nadążyć chyba jakiemukolwiek autorowi książki. Nie mamy jeszcze książki na świecie o Rails 2, (bo nawet The Rails Way opisuje wersję edge) a tu już ogłoszono Railsy w wersji 2.1.

Oczywiście wszystko to ma swoją logikę, w Portland odbywa się teraz konferencja RailsConf i jak to bywa na konferencjach, trzeba coś ogłosić - taka już natura chyba każdej większej konferencji :). Ale fakty pozostają mimo wszystko te same: framework się rozwija, co cieszy niezmiernie.

Jeśli ktoś jest żądny wiedzy co nowego w najnowszych railsach, może przeczytać wpis na oficjalnym blogu frameworka. Ja narazie w szczegóły wdawać się nie będę, bo po prostu nie czuję się kompetentny. Gorąco zachęcam natomiast do obejrzenia RailsCast'ów poczynając od #106 do #109 włącznie, gdzie Ryan Bates opisuje najnowsze featury w Rails 2.1. Warto obejrzeć wszystkie, nawet jeśli jesteś takim newbs'em jak ja ;).

Java Killers #007

Witam na kolejnej odsłonie cyklu Java Killers. Dzisiejszy przykład jest hybyrdą przykładu nadesłanego przez Damiana Szczepanika oraz małego triku, który znalazłem na sieci (niestety źródło zapodziałem :( ). Myślę, że dzisiejszy JK nie powinien sprawić kłopotu nikomu, kto zdawał SCJP, ale kto wie, moze się mylę?
Zatem spojrzmy na naszych bohaterów, trzy klasy: Matcher1, Matcher2 oraz Matcher3.


1 package javakillers.part007;
2
3 /**
4 * @author pawel
5 */
6 public class Matcher1
7 {
8 void g(int x)
9 {
10 System.err.print("int ");
11 }
12
13 void g(long x)
14 {
15 System.err.print("long ");
16 }
17
18 void g(Integer x)
19 {
20 System.err.print("Integer ");
21 }
22 }
23
24


1 package javakillers.part007;
2
3 /**
4 * @author pawel
5 */
6 public class Matcher2
7 {
8 void g(short x)
9 {
10 System.err.print("short ");
11 }
12
13 void g(byte x)
14 {
15 System.err.print("byte ");
16 }
17
18 void g(long x)
19 {
20 System.err.print("long ");
21 }
22
23 void g(Integer x)
24 {
25 System.err.print("Integer ");
26 }
27 }
28
29


1 package javakillers.part007;
2
3 /**
4 * @author pawel
5 */
6 public class Matcher3
7 {
8 void g(int x)
9 {
10 System.err.print("short ");
11 }
12
13 void g(Integer x)
14 {
15 System.err.print("Integer ");
16 }
17
18 void g(Object x)
19 {
20 System.err.print("Object ");
21 }
22 }
23
24


I na koniec metoda, która to wszystko uruchomi:


9 public static void main(String[] args)
10 {
11 int prim = 10;
12 Object obj = prim;
13 Integer intObj = (Integer) obj;
14
15 new Matcher1().g(prim);
16 new Matcher2().g(prim);
17 new Matcher3().g(obj);
18 new Matcher3().g(intObj);
19 }

Pytanie, które pozostaje otwarte, to co zwróci nam wywołanie tej metody? Odpowiedzi do wyboru aż sześć:

A) int Integer Object Integer
B) int long Object Integer
C) int Integer Integer Integer
D) int long Integer Integer
E) Integer Integer Integer Integer
E) Integer Integer Object Integer

Zanim przeczytasz rozwiązanie, spróbuj pomyśleć nad odpowiedzią.

Prawidłowa odpowiedź to oczywiście B) int long Object Integer. Aby zrozumieć czemu tak a nie inaczej należy wiedzieć, że przekazując metodzie wartość typu innego niż się spodziewała, dokonana zostaje konwersja typów (method-call conversion jeśli chcemy się trzymać terminologii). Konwersja jest możliwa tylko, jeśli typ metody jest "szerszy" (ang. widening conversion) od typu przekazywanego. Tak więc int'a przekażemy do metody z longiem ale nie na odwórt. Typy od "najwęższych" do "najszerszych" uszeregowane są następująco:

byte -> short -> int -> long -> float -> double

Ot i cała filozofia. Odnośnie ostatniego Matchera3, w pierwszym przypadku spodziewamy się obiektu klasy Object i ją dostajemy, w następnym wywołaniu obiektu klasy Integer i ją dostajemy. Mimo że jest to wciąż jeden i ten sam obiekt, to jednak kontekst wywołania jest różny, stąd i inne dopasowanie typów podczas wywoływania metody.

sobota, 31 maja 2008

Nauka Rails - 1.2.6 vs 2.0.2

Railsy mają coś w sobie! Kropka. Nikogo przekonywać nie będę. Sam widziałem po prostu kilka prezentacji, poczytałem kilka blogów, widziałem Railsy oraz Rubiego w akcji na videotutorialu, wygenerowałem sobie kilka prostych aplikacji i jestem zdeterminowany aby język Ruby oraz same Railsy poznać dokładnie.
Myślisz jak ja? Chcesz nauczyć się Railsów? Widzisz, mamy problem. Kilka miesięcy temu wyszła nowa wersja frameworka opatrzona numerkiem 2. Rails 2.0.x wprowadza trochę zmian, kilka nowości, trochę usprawnień i nie byłoby w tym nic strasznego, gdyby nie fakt, że parę rzeczy zostaje przeniesionych, inne się dewaluują, jeszcze inne całkowicie znikają. Framework nie do końca wierzy w ideę kompatybilności wstecznej. Problem jest, bo bodaj najlepsza dostępna obecnie na rynku książka "Agile. Programowanie w Rails" traktuje o Railsach w wersji 1.2.6. Początkujący programista może się zdziwić, że przykłady z pierwszych 170 stron książki w ogóle nie działają (dynamiczny scaffolding został usunięty z Railsów 2).

Pytanie pozostaje co zrobić? Na forum http://www.rubyonrails.pl temat był poruszony i większość osób zgodnie stwierdziła, że warto uczyć się Railsów z książek w wersji 1.2.6 i potem doczytać o zmianach, które zaszły w wersji drugiej. Myślę, że podejście to nie jest złe. Aby zainstalować Railsy w starszej wersji należy dodać jedną opcję więcej podczas instalacji:

gem install rails --version 1.2.6

To było proste. Jest jednak jeszcze jeden mały szczegół, o którym można zapomnieć. Tworząc nową aplikację railsową należy uwzględnić wersję. Tworząc przykładową aplikację Składnica (składnica = depot :) ) z książki "Agile. Programowanie w Rails", należy wywołać komendę rails z dodatkową opcją:

rails _1.2.6_ depot

Ja osobiście postanowiłem się uczyć Railsów z książki "Agile. Programowanie w Rails", z zainstalowanymi Railsami w wersji 2.0.2. Z większość zmian, które zaszły i o których mi wiadomo, jestem sobie w stanie poradzić:

* sqlite3 domyślną bazą danych zamiast mysql - wystarczy zainstalować sqlite3 i gotowe, nic strasznego

* brak scaffolding'u - dynamiczny scaffolding nie został tyle usunięty co przeniesiono go jako osobny plugin. Wystarczy uruchomić

script/plugin install scaffolding

aby przykład Składnica z książki APWR działał.

Zmian jest jeszcze zapewne trochę, ale myślę ze z pomocą google, poradzę sobie w trakcie napotykania problemów. Po przerobieniu APWR z pewnością przeczytam "The Ruby Way" autorstwa Obie Fernandez'a. Jedna z niewielu książek wchodząca daleko w szczegóły działania frameworka, a co ważniejsze opisującą Railsy w wersji 2.0.2.

piątek, 30 maja 2008

Instalowanie Ruby oraz Ruby on Rails na Ubuntu 8.04 Hardy Heron

Poniżej zamieszczam instrukcję instalacji języka Ruby (w wersji 1.8) oraz frameworka Ruby on Rails na najnowszej wersji Ubuntu 8.04 Hardy Heron. Podstawowa zasada (powołując się tutaj na wiki.rubyonrails.org) to instalacja Rubiego prosto z repozytorium Ubuntu, zainstalowanie gem'ów (będących repozytorium Rubiego) ze źródła, a na końcu zainstalowanie Railsów z użyciem gemów. Każda inne podejście obarczone jest jakimś problemem czy innym ryzykiem. Nie będę się wdawać w szczegóły (bo ich po prostu nie znam) i podam od razu gotowy przepis:

1. Instalacja Ruby w wersji 1.8

~$ sudo apt-get install ruby irb ri rdoc ruby1.8-dev libzlib-ruby libyaml-ruby libreadline-ruby libncurses-ruby libcurses-ruby libruby libruby-extras libfcgi-ruby1.8 build-essential libopenssl-ruby libdbm-ruby libdbi-ruby libdbd-sqlite3-ruby sqlite3 libsqlite3-dev libsqlite3-ruby libxml-ruby libxml2-dev

2. Ściągniecię i rozpakowanie źródeł, a następnie instalacja gems

~$ sudo ruby setup.rb

3. Aby mięć dostęp do komendy gem, wystarczy zwykły softlink

~$ sudo ln -s /usr/bin/gem1.8 /usr/bin/gem

4. Instlaujemy Rails'y z użyciem gems

~$ sudo gem install rails


Ot i cała magia. Tyle wystarczy aby rozpocząć swoją przygodę z Rubym i Railsami pod Ubuntu.

0 days left...

Dziś oddałem ostatni rozdział pracy magisterskiej na ręce mojej Pani Promotor. Uff... Czeka mnie teraz jeszcze cztery dni wprowadzania poprawek, zmian i sklecenie "Wstępu". Jeszcze łącznie 4 dni i po krzyku. Nie zmienia to faktu, że w końcu mogę troszkę odetchnąć i do łask (po tygodniach przerwy) wraca blog. Strona zalicza obecnie 50 wejść dziennie, co przy ostatnim braku aktualizacji cieszy mnie niezmiernie. Największą popularnością cieszy się wciąż Java Killers oraz (o dziwo?) posty o Lyx'sie - jak widać nie tylko ja doceniłem piękno tego programu:).

W każdym razie po miesiącu męczarni (nadrobieniu ostatnich 4 miesięcy pracy w ciągu zaledwie 4 tygodni) wracam do świata żywych, a wraz ze mną ten blog.

Happy :) :P ?

poniedziałek, 19 maja 2008

Back in Black!

Właśnie, po 3 miesiącach przerwy spowodowanej wiadomo czym, wróciłem ponownie na zajęcia YMAA. Trener się zdziwił jak mnie zobaczył i tym mocniej polał niemiłosiernie, jak usłyszał powód mojej absencji. Nie mniej jednak pamiętał kim jestem, a to chyba najważniejsze ;)
A trening jak to trening. Po długiej nieobecności nic się nie pamięta, formę ma się słabą, wylewa się 6 milionów litrów potu z siebie i ogrania Cię takie wszechobecne 'ale-ja-jestem-do-dupy'... Jednak nie ma nic piękniejszego jak powrót (lub próba powrotu) do domu, człowiek jest wtedy jakiś taki zmęczony, ale i rozradowany zarazem. Idzie się może włócząc trochę nogami, ale to uczucie zajechania organizmu wysiłkiem fizycznym jest po prostu tego warte.
Świat jakiś od razu wydaje się piękniejszy i to krzesło przy biurku, które jeszcze 3 godziny wcześniej właziło Ci w dupę, nagle staje się jakieś takie wygodne, miłe i przytulne... Tak tak moi mili, nie ma nic przyjemniejszego jak powrót z treningu. Wszystkim serdecznie polecam!

niedziela, 18 maja 2008

Java Killers #006

Po dłuższej przerwie, spowodowanej moją niekończącą się walką z pracą magisterską, mam przyjemność przedstawić wam koleją odsłonę cyklu Java Killers. Jak zwykle, aby nie przedłużać, spójrzmy na poniższy kod:


5 package javakillers.part006;
6
7 /**
8 *
9 * @author pawel
10 */
11 public class Main
12 {
13 public static void main(String[] args)
14 {
15 Main m = new Main();
16 m.run();
17 }
18
19 public void run()
20 {
21 int a = 0;
22 try
23 {
24 while(a < Integer.MAX_VALUE + 1)
25 {
26 a++;
27 }
28
29 if(a >= 0)
30 {
31 System.exit(0);
32 }
33 }
34 finally
35 {
36 System.out.println("a wynosi: " + a);
37 }
38 }
39 }
40
41


I standardowe pytanie: co zwróci powyższy kod. Dziś do wyboru aż pięć możliwych odpowiedzi, z czego jak zwykle tylko jedna jest prawdziwa:

A) a wynosi: 2147483647
B) a wynosi: -2147483648
C) a wynosi: 0
D) program się zapętli i sam z siebie nie zakończy działania
E) program się zakończy, ale nic nie napisze

Zanim przeczytasz rozwiązanie, spróbuj pomyśleć nad odpowiedzią.





Prawidłowa odpowiedź to oczywiście E) program się zakończy, ale nic nie napisze. Rozwiązanie jest trywialne, jeśli znane są nam dwie cechy języka Java:

1. Integer.MAX_VALUE + 1 zwróci nam liczbę ujemną [nie wiesz czemu? sprawdź w googlach, warto wiedzieć!]
2. Wywołanie System.exit(0); powoduje natychmiastowe zabicie procesu, przez co blok finally { ... } nigdy się nie wykona, mimo, że w oficjalnym tutorialu do języka Java możemy przeczytać " The finally block always executes when the try block exits". Jak widać nie zawsze i warto mieć tego świadomość.

I to koniec na dziś! Na zakończenie pytanie jeszcze dla ambitnych: kiedy jeszcze nie wykona się blok finally { ... }? Innymi słowy co, w try {... } musi się pojawić, żeby zawartość finally { ... } nigdy się nie wykonała?

sobota, 17 maja 2008

Zostań Sun Campus Ambassadorem!

Moja przygoda ze studiami informatycznymi powoli dobiega końca, a w raz z nią kończy się moja praca w Sun Microsystems jako Sun Campus Ambassador na Politechnice Wrocławskiej. W sumie w programie tym uczestniczyłem łącznie dwa lata, byłem pierwszym campus ambassadorem w Polsce oraz jednym z pierwszych na świecie. W tym czasie udało się zorganizować dwie konferencje (Sun Technology Day na Politechnice Wrocławskiej oraz wrocławską edycję Netbeans Roadshow). Mamy na kampusie prężnie działającą grupę PWr Java User Group (140 zarejestrowanych uczestników, około 50-60 aktywnych). Grupa obok krakowskiego PLJUGa i warszawskiego WarsawJUGa jest jedną z najbardziej aktywnych w kraju.

Coż udało się zrobić wiele, można było troszkę więcej, ale nie mi już będzie dane to zmieniać. Moje studia się kończą i czas przekazać pałeczkę komuś nowemu. Jest jeszcze sporo do zrobienia: grupa na Politechnice dopiero co rozwinęła skrzydła, warto by wreszcie pociągnąć grupę OpenSolarisa, stworzyć Wrocław JUG (który działać mógłby jako bliźniacza grupa do PWr JUGa lub mógłbym z PWr JUGa wyewoluować). Netbeans Road Show miał tam swój jakiś lokalny sukces, można myśleć o kolejnych edycjach, może o zorganizowaniu innych konferencji tego typu?
Pomysłów i wyzwań jest bez liku, a przecież ktoś musi im sprostać! Myślisz, że jesteś taką osobą? Chciałbyś zostać kolejnym Sun Campus Ambassadorem na Politechnice Wrocławskiej (lub Uniwersytecie Wrocławskim)? Poprowadzić community, propagować technologie związane z Javą, Ruby'm oraz Pythonem? Poznać znane osoby ze świata Javy (na przykład Roman Strobl czy Geertjan Wielenga), dużo się uczyć, doskonale bawić i dostawać jeszcze za to wynagrodzenie?
Jeśli tak lub jeśli nie jesteś do końca przekonany, ale się zastanawiasz, w takim razie serdecznie zapraszam w najbliższą środę wieczorem (21.05.2008) na nieformalne spotkanie przy piwku, na którym poopowiadam wszystkim zainteresowanym czym dokładnie praca Campus Ambassadora jest, jakie są jej zalety, jakie minusy. Dowiecie się z jakimi obowiązkami się trzeba liczyć i na jakie wynagrodzenie (nie tylko pieniężne) można liczyć. Postaram się odpowiedzieć na wszelkie Wasze pytania. Od zainteresowanych osób zbiorę dane teleadresowe potrzebne do rozpoczęcia procesu rekrutacji.

Zainteresowany/zainteresowana? Czekam zatem na Twojego maila! Wszystkie zaintrygowane osoby proszone są o napisanie do mnie na Pawel.Szulc@sun.com, celem ustalenia szczegółów. Im szybciej tym lepiej, ponieważ chciałbym zarezerwować miejsce w jakimś pubie. Wszelkie szczegóły co do czasu i miejsca spotkania będę przekazywał właśnie mailowo, dlatego wszystkich zainteresowanych, proszę o jak najszybszy kontakt.

Wszystkich serdecznie zapraszam!

środa, 7 maja 2008

Dodanie nowej klasy .cls do Lyx'a

Jeśli ktoś nie pamięta opisywałem jakiś czas temu program Lyx, służący do generowania dokumentów latexowych przy pomocy edytora bardzo zbliżonego do tego do czego zdążył nas przyzwyczaić Word czy OpenOffice Word Processor.
Dziś pokaże jak w prosty sposób dodać nową klasę latexową do programu. Wszystko w sumie zaczęło się od tego, że nie podobało mi się jak formatowana była moja praca magisterska. Zastosowałem standardową klasę book i moja praca, po wygenerowaniu pdf'a, wyglądała mniej więcej tak:


Niezadowolony wynikami poszperałem trochę sieć i znalazłem stronę, na której autor (Cezary Sobaniec) udostępnił na niej ciekawą klasę csthesis, która jak sama nazwa wskazuje przygotowana była do tworzenia informatycznych prac magisterskich.

A więc teraz pytanie jak taką klasę zainstalować? Coż przepis jest krótki:

  1. ściągnąć plik z klasą, w moim przypadku csthesis.cls i zapisać go w defaultowym katalogu latex'a (w moim przypadku był to katalog /usr/share/texmf/tex/latex/, jesli masz Ubuntu i instalowałeś Latexa przez apt-get to Twój katalog znajduje się właśnie pod tą ścieżką. Jeśli masz Windowsa, cóż radź sobie sam :). Nie wiem gdzie tam poinstalowany jest twój latex :) )
  2. ściągnąć plik z layoutem, w moim przypadku csthesis.layout i zapisać go w podkatalogu lyx'a - layouts. W moim przypadku ścieżka prowadziła do ~/lyx/layouts/
  3. w linii komend uruchomić komendę ‘texhash’
  4. przejść do lyxa, wybierać Tools > Reconfigure, po czym zrestartować program
I to tyle! Wystarczy aby cieszyć się nową klasą, która oczywiście do wyboru jest w Document->Settings->Document class. Po zmianie klasy w mojej pracy magisterskiej z book na csthesis, wygenerowany pdf wygląda teraz tak:



I nie wiem jak dla Was, ale dla mnie wygląda to o wiele lepiej.

Na końcu chciałem poruszyć jeszcze jedną sprawę. Jeśli posiadacie tylko plik z klasą (.cls), utworzenie podstawowego layout jest rzeczą szalenie prostą. Najłatwiej utworzyć nowy dokument tekstowy, wkleić poniższy tekst (zastępując xxx nazwą waszej klasy latexowej) i zapisać jako xxx.layout:

--- xxx.layout
#% Do not delete the line below; configure depends on this
# \DeclareLaTeXClass[xxx]{article (xxx)}

# Input general definitions
Input stdclass.inc

I to wszystko już na dziś. Mam nadzieję, że kogoś zainteresowałem już wcześniej programem lyx. Jeśli tak, dzisiejszy post powinien być przydatny. A teraz wracam już do mojej pracy magisterskiej, odliczanie wciąż trwa...

23 days left...

wtorek, 6 maja 2008

Java Killers #005

Witam w kolejnym cyklu z serii Java Killers, dziś sprawdzimy naszą wiedzę z zakresu prostego operatora ?. Wszyscy znamy ta konstrukcje prawda? Dla przypomnienia wygląda ona tak:

a = b ? c : d

, gdzie zależnie od wartości zmiennej b, zmienna a przyjmuje wartość c lub d. Znane? Znane! No to przejdźmy zatem do dzisiejszego przykładu:


1 package javakillers.part005;
2
3 /**
4 * @author pawel
5 */
6 public class Main
7 {
8 public static void main(String[] args)
9 {
10 Main m = new Main();
11 m.run();
12 }
13 public void run()
14 {
15 Object o = true ? new Integer(0) : new Double(0.0);
16 System.out.println("Klasa " + o.getClass().getName() + ", wartość " + o.toString());
17 }
18 }
19


Odpowiedzi standardowo do wyboru są cztery:

A) Błąd kompilacji
B) Klasa java.lang.Integer, wartość 0
C) Klasa java.lang.Double, wartość 0.0
D) Klasa java.lang.Object, wartość java.lang.Object@87816d //przy czym @87816d może mieć dowolną inną wartość

Zanim przeczytasz rozwiązanie, spróbuj pomyśleć nad odpowiedzią.



No to jak, gotowi? Otóż poprawna odpowiedź to C) Klasa java.lang.Double, wartość 0.0 . Tak tak moi mili Object o = true ? new Integer(0) : new Double(0.0) zwróci nam obiekty typu Double. Pytanie "ale czemu?" pozostaje wciąż otwarte, gdyż na logikę widzimy, że referencja powina wskazywać na obiekt typu Integer!

Żeby zrozumieć dokładnie co stało się za naszymi plecami w czasie wykonywania kodu, należy zajrzeć do specyfikacji języka Java, a konkretnie do rozdziału '15.25 Conditional Operator ?', gdzie czytamy, że:

" (...) if the second and third operands have types that are convertible to numeric types, then (...) binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion and value set conversion."

Czy widzicie już zło jakie czai się za tym zdaniem? Kompilator orientuje się, że zwrócony wynik będzie zawsze typem numerycznym (Integer lub Double). W czasie działania programu dokonuje unboxingu do wartości prymitywnych, następuje promocja typu (w tym wypadku z int'a na double) i ponowny boxing do obiektu już typu Double a nie Integer!
Żeby to lepiej zobrazować, zmieńmy w naszym kodzie new Integer(0) na new Integer(1)


13 public void run()
14 {
15 Object o = true ? new Integer(1) : new Double(0.0);

Po uruchomieniu dostaniemy odpowiedź Klasa java.lang.Double, wartość 1.0. Widzicie co się dzieje?

Co ciekawe jeśli uruchomicie poniższe przykłady pod Eclipsem, wyskoczy Wam błąd kompilacji! Eclipse używa swojego własnego kompilatora (innego niż javac dostarczony z JDK), który jak tylko zobaczy Object o = true ? new Integer(1) : new Double(0.0); krzyczy "Ja nic nie wiem, ja nic nie chce, ja nic nie umiem!!!" Na innych IDE, takich jak Netbeans czy IntelliJ IDEA, kompilacja przebiega poprawnie, a wyniki działania programów są takie jak opisane powyżej.

Teraz się przyznać, ile osób znało poprawną odpowiedź? :)



poniedziałek, 5 maja 2008

Ruby on Rails na Glassfish'u

W ostatnim poście wspomniałem o możliwości uruchamiania aplikacji napisanych w Ruby on Rails na javowych serwerach aplikacji (przy pomocy JRuby) oraz idei jaka przyświeca takiej migracji. Jeśli spojrzymy sobie na preview nadchodzącego Glassfis'a V3 dowiemy się, że jedną z nowości (poza takimi oczywistościami jak wsparcie dla JEE6 :) ), będzie możliwość uruchamiania aplikacji napisanych w dynamicznych językach takich jak Groovy czy Ruby.

Ale Glassfish V3 to przecież jeszcze daleka przyszłość i pozostaje pytanie czy obecnie można wrażać już aplikacje railsowe na obecne wydanie serwera. Odpowiedź brzmi tak, na szczęście jest już możliwość uruchamiania aplikacji RoR i wsparcie dla JRuby w Glassfish'u V2. Wprawdzie jest to feature, który nie przychodzi ze standardową instalacją, ale dołączenie go do działającej instancji jest tak na prawdę kwestią kilku kliknięć.

W katalogu glassfisha uruchamiamy narzędzie updatetool, służące do aktualizowania zainstalowanych wtyczek w serwerze aplikacji

./updatecenter/bin/updatetool

W uruchomionym programie zaznaczamy zakładkę 'JRuby on Glassfish', warto zwrócić uwagę czy jest w wersji 3.0.

Klikamy Install i gotowe! Teraz w katalogu glassfisha pojawił nam się nowy folder o nazwie jruby, którego zawartość widzimy poniżej


Mamy tutaj zarówno najnowszego JRuby (jruby-1.1.1) jak i katalog z przykładami (samples). Właśnie jednym z zamieszczonych przykładów posłużymy się demonstrując działanie RoR na Glassfish'u. Wchodzimy do katalogu HelloWorldRailsApp, który jest zwykłą aplikacją railsową typu hello-world - czyli jeden kontroler "say", z akcją hello i odpowiadającym mu widokiem. Następnie tworzymy z tej aplikacji war'a przy pomocy narzędzia dostarczonego wraz z JRuby w wersji 1.1.1 o nazwie "warable":

cd jruby/samples/HelloWorldRailsApp
jruby -S warble
Tak stworzonego war'a możemy w sposób standardowy zdeployować (wdrożyć) na naszym serwerze

i już po możemy cieszyć się naszą aplikacją railsową działającą w środowisku glassfisha

I to wszystko!

Jest jeszcze tylko jedno małe ale. Ponieważ warable pakuje całego rubiego i gemy do pojedyńczego war'a (wraz z naszą aplikacją), jest to rozwiązanie dobre tylko na szybkie protypowanie czy zapoznanie się z możliwościami JRuby na Glassfishu. Na dłuższą metę warto jednak przerzucić całą zawartość jruby do katalogu glassfish/lib a deployment robić poprzez uruchamianie komendy create-shared, która w katalogu aplikacji railsowej tworzy WEB-INF/web.xml. Tak przygotowaną aplikacje można już wdrażać na serwer.

JRuby on Glassfish 3.0 dostarcza nam środowisko JRuby w wersji 1.1.1 oraz Ruby on Rails w wersji 2.0.2. Wszystko szybko, łatwo i przyjemnie, dlatego polecam spróbować.

niedziela, 4 maja 2008

Po co nam JRuby?

Kończenie pracy magisterskiej ma jedną poważną wadę - monotonię. Przez ostatnie dwa miesiące nie robiłem nic innego jak próbowałem zakończyć prace nad programem, co wciąż czynie mimo, że termin zbliża się nieubłaganie ku końcowi. Ból jest taki, że nie robi się nic innego, tylko tą pracę magisterską i taka stagnacja i marazm mogą zabić.
Dlatego postanowiłem troszkę rozerwać mój umysł czymś innym i w wolnych chwilach zacząłem przyglądać się językowi Ruby oraz framework'owi Ruby on Rails. I to właśnie prostota i przejrzystość RoR ciekawiła mnie bardziej niż sam język Ruby. W niedługim czasie jednak posmakowałem jednak samego języka i zadowoleniem musze stwierdzić, że coś w Rubym jest jednak takiego nienazwanego, że odczuwa się pewną lekkość i przyjemność w klepaniu kodu.
Jakiś czas temu pojawiła się na horyzoncie implementacja Ruby'iego napisana całkowicie w Javie. JRuby, bo o nim właśnie mowa, to produkt open source, ale powstał pod skrzydłami Sun Microsystems. Ktoś może się zastanawiać czy na prawdę potrzebujemy warstwy pośredniej w postaci maszyny wirtualnej, aby pisać aplikacje w Ruby'm? Oczywiście odpowiedź brzmi nie, nie potrzebujemy. Wykonanie programu typu przeiteruj się po kolekcji, wykonaj na elementach jakieś obliczenia, zwróć wynik, wykona się zdecydowanie szybciej w standardowym ruby niż w jruby, gdyż do tego typu zadań został stworzony właśnie interpreter ruby'ego.

Problem zaczyna się, gdy chcemy ruszyć coś większego, używając choćby Ruby on Rails jako frameworka do pisania aplikacji webowych. Jeśli czytaliście kiedyś rozmowy o Ruby on Rails, powtarzającym się bez końca tematem jest skalowalność aplikacji napisanych w frameworku. Problem wydaje się rzeka, ale z drugiej strony trudno się dziwić. Jeśli ktoś chce napisać taki powiedzmy nowej generacji serwis społecznościowy, kwestia wydajności przy dużym obciążeniu serwisu wydaje się problemem istotnym. Wtedy właśnie z pomocą może nam przyjść JRuby. Ponieważ implementacja napisana jest w 100% w Javie, możemy nasze aplikacje railsowe odpalać na serwerach aplikacji, które dotąd przeznaczone były dla rozwiązań JEE5. A te z kolei znane są właśnie ze skalowalności, o która tak bardzo drży świat enterprise. Ogólne założenie jest takie jak na rysunku poniżej


Rozwiązanie logiczne i co więcej sprawdza się na tyle, że przewiduje się, że w niedalekiej przyszłości w świecie RoR, JRuby będzie wiódł prym, powoli wygryzając rozwiązania serwerowe pure-ruby takie Mongrel. Czy będzie tak w rzeczywistości trudno powiedzieć, myślę, że sprawa jest jeszcze świeża. Jeśli jednak dołożymy do całości takie smakołyki jak możliwość zamykania logiki biznesowej w javowe obiekty EJB, które wywoływać można (zdalnie i lokalnie) z poziomu kodu ruby'ego, tworzy nam się całkiem ciekawe rozwiązanie.

O JRuby'm można poczytać na blogu Wiktora Gworka, który chyba na poważnie zajął się ewangelizacją języka. Jeśli starczy mu zapału stworzy nam niedługo cykl, będący wstępem do JRubiego, jak narazie, można poczytać pierwszy odcinek.
Jeśli tematyka kogoś interesuje, a angielski mu nie straszny, warto zajrzeć również tu, tu i tu.

No i to tyle na dziś. Wracam do pracy magisterskiej, zostało tylko 25 dni...

update: jak w prosty sposób zainstalować wsparcie dla JRuby na Glassfishu opisałem tutaj