Pokazywanie postów oznaczonych etykietą guice. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą guice. Pokaż wszystkie posty

28 grudnia 2010

Google Guice z Clojure - niebagatelna rola IRC w moim rozwoju

3 komentarzy
Do moich poczynań z JRuby on Rails i komentarzy do wpisu Świąteczne próby z JRuby on Rails wrócę w kolejnym wpisie. Dzisiaj będzie o czymś troszkę innym.

Zdumiewające, jak wiele wiedzy można zdobyć stosując wciąż konwencjonalne sposoby nauki. IRC to narzędzie stare jak sam Internet i pamiętam, ile sprawiało mi przyjemności i satysfakcji, kiedy spędzałem godziny siedząc przed terminalem na uczelni klepiąc dyskusje na jakimś bliżej nieokreślonym kanale. Nie miało znaczenia, gdzie, z kim i o czym, byle można było odsiedzieć swoje przez terminalem. Człowiek nic nie robi i czas mu leci - wtedy to był boski stan.

Przez długi czas nie uważałem IRCa za poważne narzędzie. Kojarzyło mi się właśnie z tym błogim stanem nic-nie-robienia, bo w zasadzie w Sieci jeszcze niewiele można było wtedy robić. Kiedy przyłączyłem się do Apache OpenEJB moje postrzeganie IRCa nieznacznie przesunęło się w stronę większego użycia przy rozwiązywaniu problemów - łatwiej było dopytać o szczegóły działania tego czy innego ustrojstwa. Raczej z rzadka tam zaglądałem, bojąc się nieprzerwanych dyskusji, które fajnie było prowadzić, ale w podsumowaniu wyglądały mizernie.

Nie wiem dlaczego, ale postanowiłem uruchomić Google Guice z poziomu Clojure. Czasami takie pomysły są zarzewiem kolejnych, więc nie zastanawiając się długo, zabrałem się za temat.

Początek był trywialny. Chwila zastanowienia i...potrzebny jest Guice w CLASSPATH do Clojure REPL. Nie inaczej, kiedy klasa javowa chce wywołać inną - obie muszą (zwykle) siebie widzieć przez CLASSPATH. Uruchamiam Clojure 1.3.0-alpha4 z odpowiednimi bibliotekami Guice.
jacek:~
$ CLASSPATH=~/apps/guice/guice-3.0-rc1.jar:~/apps/guice/javax.inject.jar:~/apps/guice/aopalliance.jar clj -13
CLOJURE_DIR:  /Users/jacek/apps/clojure
CLOJURE_CONTRIB_JAR:  /Users/jacek/apps/clojure-contrib-1.3.0-alpha3.jar
Clojure 1.3.0-alpha4
user=>
Pierwsza biblioteka oczywista, a dwie pozostałe wyszły "w praniu", kiedy kolejno uruchamiałem potrzebne elementy Guice.

Skracając znacznie moją historię, dotarłem do momentu, w którym miałem następujący stan:
user=> (import '(com.google.inject Guice AbstractModule))
com.google.inject.AbstractModule
user=> (def module (proxy [AbstractModule] [] (configure [] (println "configure called"))))
#'user/module
user=> (Guice/createInjector module)
IllegalArgumentException No matching method found: createInjector  clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:77)
I tu mała zagwozdka. W javadoc dla com.google.inject.Guice jest metoda createInjector, która powinna akceptować mój moduł. Zresztą niejedna.

I tak stan dumania, co by tu począć, trwał cały dzień. W zasadzie trochę dłużej, bo już wczoraj odnotowałem sobie, gdzie stanąłem i zabrałem się za dalsze dumanie z samego rana.

Wieczorem miałem dosyć. Postanowiłem napisać na grupę użytkowników clojure i iść spać. I już miałem pisać, kiedy przypomniałem sobie o kanale #clojure. I to był strzał w dziesiątkę!
[11:22pm] jlaskowski: hi
[11:23pm] jlaskowski: I'm having troubles with java.lang.IllegalArgumentException: No matching method found: createInjector
[11:23pm] jlaskowski: CLASSPATH=`pwd`/guice/guice-3.0-rc1.jar:`pwd`/guice/javax.inject.jar:`pwd`/guice/aopalliance.jar clj -13
[11:23pm] jlaskowski: CLOJURE_DIR:  /Users/jacek/apps/clojure
[11:23pm] jlaskowski: CLOJURE_CONTRIB_JAR:  /Users/jacek/apps/clojure-contrib-1.3.0-alpha3.jar
[11:23pm] jlaskowski: Clojure 1.3.0-alpha4
[11:23pm] jlaskowski: (def mm (proxy [AbstractModule] [] (configure [] (println "configure called"))))
[11:23pm] jlaskowski: (bean mm)
[11:24pm] jlaskowski: {:class user.proxy$com.google.inject.AbstractModule$0}
[11:24pm] jlaskowski: and when I call (Guice/createInjector mm)
[11:24pm] jlaskowski: it spits out the exception
[11:24pm] jlaskowski: http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/AbstractModule.html
[11:25pm] jlaskowski: how can I find out what is wrong exactly?
[11:25pm] jlaskowski: how do I find out what Clojure can call upon a class?
[11:26pm] jlaskowski: any helpful reflection methods to use?
[11:26pm] amalloy: jlaskowski: createInjector takes an array of Modules, not a single module
[11:27pm] amalloy: java's Foo.bar(Whatever...) is sugar for arrays
[11:27pm] jlaskowski: right
[11:27pm] jlaskowski: but have a look at the full stack trace
[11:27pm] jlaskowski: user=> (.printStackTrace *e)
[11:27pm] jlaskowski: java.lang.IllegalArgumentException: No matching method found: createInjector
[11:27pm] jlaskowski: at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:77)
[11:27pm] jlaskowski: at clojure.lang.Reflector.invokeStaticMethod(Reflector.java:202)
[11:27pm] jlaskowski: at user$eval17.invoke(NO_SOURCE_FILE:4)
[11:27pm] jlaskowski: at clojure.lang.Compiler.eval(Compiler.java:6201)
[11:27pm] jlaskowski: at clojure.lang.Compiler.eval(Compiler.java:6168)
[11:27pm] jlaskowski: at clojure.core$eval.invoke(core.clj:2680)
[11:27pm] jlaskowski: at clojure.main$repl$read_eval_print__5619.invoke(main.clj:179)
[11:27pm] jlaskowski: at clojure.main$repl$fn__5624.invoke(main.clj:200)
[11:27pm] jlaskowski: at clojure.main$repl.doInvoke(main.clj:200)
[11:27pm] jlaskowski: at clojure.lang.RestFn.invoke(RestFn.java:422)
[11:27pm] jlaskowski: at clojure.main$repl_opt.invoke(main.clj:266)
[11:27pm] jlaskowski: at clojure.main$main.doInvoke(main.clj:361)
[11:27pm] jlaskowski: at clojure.lang.RestFn.invoke(RestFn.java:437)
[11:27pm] jlaskowski: at clojure.lang.Var.invoke(Var.java:409)
[11:27pm] jlaskowski: at clojure.lang.AFn.applyToHelper(AFn.java:169)
[11:27pm] jlaskowski: at clojure.lang.Var.applyTo(Var.java:518)
[11:27pm] jlaskowski: at clojure.main.main(main.java:37)
[11:27pm] amalloy: augh do not do that
[11:27pm] amalloy: http://gist.github.com
[11:28pm] jlaskowski: amalloy: ok, will use it
[11:29pm] amalloy: anyway, yes. it can't find a method with that name whose parameters are (Foo), only one whose parameters are (Foo[])
[11:29pm] jlaskowski: you're right!
[11:29pm] jlaskowski: so how can I call a method that accepts an array?
[11:29pm] amalloy: you can find this out for yourself with clojure.contrib.repl-utils/show
[11:30pm] amalloy: jlaskowski: ##(doc into-array) is one way; in this case it looks like Guice will also accept an Iterable, so you can just pass it a clojure vector
[11:30pm] sexpbot: ⟹ "([aseq] [type aseq]); Returns an array with components set to the values in aseq. The array's component type is type if provided, or the type of the first value in aseq if present, or Object. All values in aseq must be compatible with the component type. Class objec... http://gist.github.com/756642
[11:31pm] jlaskowski: HURRAYY - you saved my day, amalloy
[11:31pm] jlaskowski: it works fine now
[11:32pm] Luyt_: Freenode's channels are a great resource, because of all the friendly and knowledgeable people on it!
[11:32pm] jlaskowski: it's awesome how quickly it's sorted out!
[11:32pm] Luyt_: ...and some communities are really great.
[11:32pm] lancepantz: we just have a great community 
[11:32pm] jlaskowski: it's not the first time I just enter the channel
[11:33pm] jlaskowski: and get an answer in secs
[11:33pm] jlaskowski: unbelievable
[11:33pm] jlaskowski: thanks again
[11:34pm] Luyt_: as long as you don't become a Help Vampire (http://slash7.com/2006/12/22/vampires/) it's allright 
[11:35pm] Luyt_: and you can always give back to the community, of course. Like lurking and answering questions from noobs, which are too tedious for the advanced clojurists in here. (Same works in #python)
I to było dokładnie to, czego potrzebowałem! Niecały kwadrans i odpowiedź już jest. W zasadzie odpowiedź była minutę po tym, kiedy skończyłem pisać - użyć tablicy zamiast po prostu przekazywać moduł. Ech, człowiek ślepnie na starość.

Wystarczy użyć operatora tablicowego i Guice będzie kontent.
user=> (Guice/createInjector [module])
configure called
#<InjectorImpl Injector[bindings=[ProviderInstanceBinding[key=Key[type=com.google.inject.Injector, annotation=[none]], source=[unknown source], scope=Scopes.NO_SCOPE, provider=Provider<Injector>], ProviderInstanceBinding[key=Key[type=java.util.logging.Logger, annotation=[none]], source=[unknown source], scope=Scopes.NO_SCOPE, provider=Provider<Logger>], InstanceBinding[key=Key[type=com.google.inject.Stage, annotation=[none]], source=[unknown source], instance=DEVELOPMENT]]]>
Tylko, po co mi to było?! :)

15 lipca 2010

Google Guice i JSR-330 Dependency Injection for Java

6 komentarzy
W Recenzja "Dependency Injection, Design patterns using Spring and Guice" z Manning pisałem o moim zachwycie Google Guice. Okazało się, że jest to jedno z tych rozwiązań, którego istnienia nie doceniałem, a gdyby zapytać mnie o cechy wyróżniające go od innych, podobnych jemu, z pewnością nie potrafiłbym wskazać tych, które mogłyby sprawić, że stanie się właściwym rozwiązaniem w projekcie. Obecna moja znajomość Guice sprowadza się do umiejętności wymówienia jego nazwy i opisania jako kontener DI (ang. dependency injection). Niewiele.

Po tej książce, potrzebowałem bliższego, bardziej praktycznego spotkania z Guice. Kiedykolwiek myślę o kontenerze DI, na myśl przychodzi Spring Framework (czasami, ale baaaardzo rzadko, takie egzotyczne rozwiązania jako HiveMind, PicoContainer, może nawet JBoss Seam, Apache XBean czy dawno zapomniany Avalon). Moje ostatnie dokonania na gruncie Spring Framework w postaci warsztatu i po nim następujących artykułów (patrz Tworzenie samodzielnej aplikacji ze Spring Framework i Hibernate w NetBeans IDE 6.9) trochę mnie zmęczyły i kiedykolwiek zabierałem się za kolejną konfigurację w XMLu, wspominałem stare, dobre czasy z serwerem aplikacyjnym Java EE. Ktoś mógłby wspomnieć o możliwości konfiguracji Springa przez adnotacje, ale jakby tego nie ująć, każdorazowe spotkanie ze Springem zawsze u mnie kończy się jednak na XMLu. Brrr...

Kiedy zabrałem się za poznawanie specyfikacji Java EE 6, zacząłem od JSR 299: Contexts and Dependency Injection for the Java EE platform (w skrócie CDI). Tam naczytałem się o wielu innych specyfikacjach i czego mi brakowało, to jego użycia poza serwerem aplikacyjnym JEE6. W końcu jakoś się udało i nawet spisałem moje doświadczenia w artykule Contexts and Dependency Injection (CDI) praktycznie - zestawienie środowiska z JBoss Weld, Arquillian i Apache Maven 2. Należy jednak zwrócić uwagę na słowo "jakoś", bo ono oddaje klimat tego artykułu - CDI w obecnym kształcie jest możliwe do uruchomienia poza serwerem aplikacyjnym, ale jego główne użycie jest właśnie w ramach serwera (chociażby JSF2). Nie pasowało mi tu stworzenie kolejnej specyfikacji JSR-330 Dependency Injection for Java. Możnaby powiedzieć, że DI to CDI, ale coś je jednak musi różnić, skoro obie istnieją pod parasolem JEE6. I różnią się - zespołem pracującym nad nimi :-) Przy CDI pracował Gavin King (JBoss Seam, Hibernate), a przy DI pracowali Bob Lee (Guice) i Rod Johnson (współtwórca Spring Framework). To może tłumaczyć pojawienie się obu, zamiast stworzenia jednej właściw(sz)ej.

Ostatnio zasugerowano mi, aby sprawdzić wsparcie CDI przez Springa i przez myśl przeszło mi, aby się tym zająć, ale zamiast CDI skończyło się na JSR330 DI. Zacząłem od dokumentacji pakietu javax.inject. To właśnie wtedy stwierdziłem, aby zamiast Springa, zbadać Guice.

Po javadoc wzięło mnie na lekturę Dependency injection with Guice, który okazał się bardzo przystępny  merytorycznie, wciąż jednak stosunkowo krótki, aby się nie znużyć jedynie czytaniem. Później przyszła pora na Google Guice :: Getting Started i Google Guice :: Spring Comparison. Z nimi i wcześniejszą lekturą książki Dependency Injection - Design patterns using Spring and Guice byłem gotów do wstępnych testów, tym razem z IntelliJ IDEA.

Zacznijmy od końca - od uruchomienia Guice i pobrania kompletnego obiektu z już wstrzelonymi zależnościami.
package pl.jaceklaskowski.javaee.di;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class Main {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new DictionaryModule());
        EnglishPolishDictionaryService dictionaryService = injector.getInstance(EnglishPolishDictionaryService.class);
        System.out.println(dictionaryService.translate("turn out"));
    }
}
Konfigurację Guice realizuje się przez moduły - klasy javowe, w których przypisujemy interfejsy do ich implementacji, typów, które będą przedmiotem wstrzeliwania zależności.
package pl.jaceklaskowski.javaee.di;

import com.google.inject.AbstractModule;

public class DictionaryModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(VocabularyProvider.class).to(EnglishVocabularyProvider.class);
    }
}
Każdy moduł rozszerza klasę com.google.inject.AbstractModule. To w module konfigurujemy Guice na potrzeby naszej aplikacji, gdzie następuje przypisanie co będzie przypisane gdzie za pomocą konstrukcji
bind([GDZIE]).to([CO]).in([OBSZAR])
W powyższym przykładzie, jakiekolwiek wystąpienie zmiennej typu VocabularyProvider zostanie zainicjowane zmienną typu EnglishVocabularyProvider, domyślnie jako singleton.

Ostatnim, aczkolwiek najważniejszym elementem aplikacji korzystającej z Guice, jest wykorzystanie adnotacji @com.google.inject.Inject, która oznacza miejsca podlegające mechanizmowi wstrzeliwania zależności (stąd też nazwa dla specyfikacji JSR 330 DI - atInject). Poniższa klasa prezentuje adnotację @Inject w akcji.
package pl.jaceklaskowski.javaee.di;

import com.google.inject.Inject;

import java.util.Locale;
import java.util.Map;

public class EnglishPolishDictionaryService implements DictionaryService {

    private final Map<String, String> vocabulary;

    @Inject
    EnglishPolishDictionaryService(VocabularyProvider vocabularyProvider) {
        this.vocabulary = vocabularyProvider.provideVocabularyFor(Locale.ENGLISH);
    }

    public String translate(String word) {
        return "[PL]" + word; 
    }
}
Każde wystąpienie @Inject jest miejscem, gdzie nastąpi wstrzelenie zależności przez Guice. Czy to konstruktor (którego wszystkie parametry podlegają wstrzeliwaniu przez Guice), czy metoda (o dowolnej nazwie i sygnaturze), czy pole instancji, wszystkie te miejsca są inicjowane przez Guice.

p.s. W ostatniej klasie EnglishPolishDictionaryService popełniłem błąd związany z niepoprawnym użyciem wstrzeliwania zależności (oferowanym przez Guice, ale to nie ma w tym przypadku znaczenia). Sama aplikacja działa, ale właściwe użycie DI powinno odpowiadać zasadzie Inject only direct dependencies. Przykład obrazuje, ile jeszcze przede mną nauki, aby tę całą tajemną wiedzę przyswoić.

21 czerwca 2010

Recenzja "Dependency Injection, Design patterns using Spring and Guice" z Manning

3 komentarzy
Cóż za niesamowita książka! Jak wspominałem w "Sport to zdrowie", a na mojego nosa jest jednak inaczej, w trakcie oczekiwania na wizytę w gabinecie laryngologicznym dotarłem do końca Dependency Injection, Design patterns using Spring and Guice autorstwa Dhanji R. Prasanna (Manning, sierpień 2009) i jestem nią wprost urzeczony.

Książka naładowana wiedzą dotyczącą koncepcji wstrzeliwania zależności (ang. dependency injection) oraz tematów wspierających jak zarządzanie stanem i wzorców programistycznych - proxy, adapter i provider na przykładach ze Spring Framework i Google Guice (głównie jednak tego drugiego, Guice). Nieoczekiwanie zrozumiałem podstawy do stworzenia trzech specyfikacji Java EE 6 - JSR-299: Contexts and Dependency Injection for the Java EE platform (CDI)JSR 330: Dependency Injection for Java i JSR-316: Java EE 6 Managed Beans, a tym samym zdobyłem garść pożytecznych informacji na tematy, o których jeszcze przed miesiącem mogłem jedynie pomarzyć. Takie akcje lubię najbardziej - robię jedno, a kończę wiele.

Książkę trafiła w moje ręce w ramach programu "Książka dla JUGa za recenzję" i nie potrafię wytłumaczyć, dlaczego zdecydowałem się na jej lekturę. Jest kilka książek, które czekają na mnie, ale akurat padło na nią. Nie żałuję tej decyzji i dziwię się, że nie zabrałem się za nią wcześniej. Ech, gdybym tylko wiedział...Książka trafiła na półkę Biblioteki Warszawskiego JUGa i czeka na kolejnego zainteresowanego zgłębieniem tajników wstrzykiwania zależności oraz podstaw technologicznych stojących za stworzeniem wspomnianych specyfikacji Java EE 6. Dodatkowo można w bardzo przyjemny sposób zapoznać się z Google Guice, co jest o tyle niebezpieczne, że można stać się jego zagorzałym zwolennikiem. To, co mogłem wyczytać o Guice z tej książki z pewnością plasuje mnie w gronie takowych. Ostrzegałem.

Zainteresowanych angielskojęzyczną recenzją zapraszam do mojego artykułu na Wiki - Book review: Dependency Injection.

Jeszcze nie wybrałem kolejnej książki, mimo, że czeka kilka, ale chętnie wysłucham sugestii. Tylko proszę nie wspominać o Clean Code: A Handbook of Agile Software Craftsmanship, bo już zamówiona i pewnie zabrałbym się za nią, gdybym ją miał pod ręką. Wersja polska Czysty kod. Podręcznik dobrego programisty również w drodze.

p.s. Czytałem dzisiaj również bardzo ciekawy artykuł Functional Programming For The Rest of Us nt. programowania funkcyjnego (PF) i wciąż nie mogę otrząsnąć się z wrażenia, jak fascynujący świat kryje się za nim. Chciałbym go skosztować, ale najwyraźniej brakuje mi zapału. Gdyby tak zechciał znaleźć się mentor, któremu udałoby przeciągnąć mnie przez meandry myślenia OO ku PF byłoby cudnie. Clojure preferowany.

p.s. Zostały jedynie 4 dni do naszego społecznościowego święta - konferencji Javarsovia 2010! Koniecznie musimy obgadać to i owo. Gotowym na zaczepki - wystarczy przywitać się (mile widziane komentarze, jakie to fajne rzeczy opisuję na blogu :)), ale można i zagadnąć o cokolwiek ze świata Javy. Chętnie poznam Wasze opinie, które sprawią, że moje pomysły nabiorą rumieńców.