28 kwietnia 2009

Groovy przez pryzmat makr OpenOffice.org

Niesamowite rzeczy można wyczyniać w tym Groovy. Wciąż nie przestaje mnie zadziwiać. Tym razem natrafiłem na pewną cechę Groovy przez...OpenOffice.org.

Na tapetę poszedł temat pisania skryptów w OpenOffice.org (OOo). Korzystam z OOo Calc i wciąż przeklejam dane z Sieci, aby coś tam wyliczać, więc pomyślałem, że mógłbym to zautomatyzować. Przejrzałem Sieć w poszukiwaniu informacji nt. pisania skryptów w OOo (dokładniej OpenOffice.org Scripting Framework) i wyszło mi, że poza Javą, JavaScript i Python można również pisać w Groovy za pomocą rozszerzenia Groovy for OpenOffice. Właśnie! Groovy! Tego mi było trzeba. Po instalacji wtyczki G4OOo, która sprowadza się do wykonania kilku czynności z poziomu OOo, pozostało napisać skrypt. I tu zaczęła się walka z poznawaniem OOo API. Już dawno mnie tak nic nie zmęczyło. Normalnie koszmar. Przypomniały mi się czasy programowania w technologii CORBA. Na szczęście z pomocą przyszedł Groovy, który męki z OOo API przesłonił Groovy Categories. Jest tam wzmianka o Objective-C, którego nie miałem okazji wcześniej poznać (a widać powinienem, skoro są tam takie cuda), więc tym bardziej zaciekawiło mnie, cóż to są te kategorie. Na zachętę zacytuję tylko wycinek wprowadzenia do kategorii Groovy na wspomnianej stronie:

There are many situations where you might find that it would be useful if a class not under your control had additional methods that you define.

W świecie AOP nazywa się to bodajże Mixin. Bez względu jak by to nie nazwać łącząc to z OOo API zamiast pisać:
 def doc = XSCRIPTCONTEXT.document
def spreadsheet = UnoRuntime.queryInterface(XSpreadsheetDocument.class, doc)
można tak:
 class UnoCategory {
public static Object uno(Object unoObj, Class clazz) { UnoRuntime.queryInterface(clazz, unoObj) }
public static Object getAt(XPropertySet pset, String pname) { pset.getPropertyValue(pname) }
public static void putAt(XPropertySet pset, String pname, Object newValue) { pset.setPropertyValue(pname, newValue) }
public static Object getAt(XIndexAccess ndx, int x) { ndx.getByIndex(x) }
}

use (UnoCategory) {
def doc = XSCRIPTCONTEXT.document
def spreadsheet = doc.uno(XSpreadsheetDocument)
}
Na tym przykładzie możnaby pomyśleć, że pisania jest znacznie więcej, ale tak nie jest w rzeczywistości. Wystarczy kilkakrotnie wykonać metodę uno() zamiast jej równoważnika UnoRuntime.queryInterface(clazz, unoObj), aby natychmiast zauważyć różnicę. W moim niewielkim skrypcie korzystam z tej metody kilkakrotnie, a poza wypisaniem tekstu "Groovy rulez!" w komórce (5,1) nic nie robię - w końcu wciąż się tego uczę, więc czego się spodziewaliście?! :)
 import com.sun.star.uno.UnoRuntime
import com.sun.star.sheet.XSpreadsheetDocument
import com.sun.star.sheet.XSpreadsheet
import com.sun.star.lang.XMultiComponentFactory
import com.sun.star.awt.XDialogProvider
import com.sun.star.beans.XPropertySet
import com.sun.star.container.XIndexAccess

class UnoCategory {
public static Object uno(Object unoObj, Class clazz) { UnoRuntime.queryInterface(clazz, unoObj) }
public static Object getAt(XPropertySet pset, String pname) { pset.getPropertyValue(pname) }
public static void putAt(XPropertySet pset, String pname, Object newValue) { pset.setPropertyValue(pname, newValue) }
public static Object getAt(XIndexAccess ndx, int x) { ndx.getByIndex(x) }
}

use (UnoCategory) {
// kontekst jest dostępny wszystkim skryptom
def doc = XSCRIPTCONTEXT.document

def spreadsheet = doc.uno(XSpreadsheetDocument)
def sheets = spreadsheet.sheets
def sheetGospodarka = sheets.getByName("Gospodarka").uno(XSpreadsheet)
sheetGospodarka.getCellByPosition(5,1).setFormula("Groovy rulez!")

// skrypt powinien zwrócić 0 jako poprawne wykonanie
0
}
Nie agitując dalej, warto się przyjrzeć kategoriom w Groovy - zdaje się, że jest to pomost do świata zaawansowanego AOP, a zaczęło się tak niewinnie - napisanie skryptu w OOo. Interesujące (a książka Programming Groovy z The Pragmatic Programmers wciąż czeka na mnie cierpliwie).

Swój skrypt-makro umieściłem w głównym menu, więc pozostaje go tylko dokończyć o pobieranie danych z Sieci i finito! Więcej informacji o strukturze arkuszy w OOo Calc znajdziemy w Working with Spreadsheet Documents, a przykładowe skrypty są w dawno nieodświeżanym serwisie OO-Snippets.

Jakby tego było mało, trafiła mi się jeszcze jedna ciekawostka Groovy. Podczas próby uruchomienia wątków za pomocą konstrukcji
 Thread t = new Thread() {
public void run() {
// zrób coś
}
}
t.start()
jedyne, co otrzymywałem, to komunikat błędu (nie ma sensu go tu przywoływać, taki był dziwaczny). Przypomniałem sobie, że w kontekście integracji Wicket i Grails wspominano coś o braku wsparcia dla klas wewnętrznych (w tym przypadku klas anonimowych), więc spróbowałem tak:
 class MyRunnable implements Runnable {
public void run() {
// zrób coś
}
}
Nie miałem pojęcia, czy tak można, czy nie, ale tym razem komunikat błędu był najwyższych lotów:

Class definition not expected here. Possible attempt to use inner class. Inner classes not supported, perhaps try using a closure instead.

To mnie skierowało, aby oprogramować to w jeszcze inny sposób:
 Thread t = new Thread({
// zrób coś
})
t.start()
I to zadziałało! I jak tu nie lubić Groovy'ego?! Mimo potencjalnego braku szybkości działania, w przypadku pisania skryptów OOo jest w zupełności wystarczający. Nie potrzeba przecież wyrafinowanego wydajnościowo środowiska - Groovy nadaje się w tym przypadku idealnie. Wystarczy, aby pisało się szybko i bez karbów silnego typowania.

Dla odświeżenia umysłu warto otworzyć OOo Calc i wpisać =starcalcteam() w dowolną komórkę. Interesujące, co?! Więcej na Easter Egg: starcalcteam().

15 komentarzy:

  1. Nie jest to mixin.

    Groovy wspiera mixins na dwa sposoby, więc Twój kod można przepisać bez używania "use" :)


    http://www.infoq.com/articles/groovy-1-6
    Tu są opisane dwa sposoby (@Mixin i Runtime mixins) opisane wraz z @Category ułatwiająca jeszcze bardziej tworzenie kategorii.

    Pozdrawiam,
    Krzysztof Kowalczyk

    OdpowiedzUsuń
  2. Ciekawe, ciekawe. Jedyny znany mi teraz problem, to Groovy 1.5.6 w G4OOo zamiast 1.6. Ciekawe rzeczy można wyczyniać w Groovy. Do czego go używasz?

    OdpowiedzUsuń
  3. Cała seria o Groovym i Grailsach bardzo ciekawa, a z każdym Twoim postem zaczynam się coraz bardziej zastanawiać nad lekką zmianą tematu magisterki, z porównania Javowych frameworków front-endowych na porównanie Wicketa z Groovy/Grails i czymś jeszcze, może Django, może Railsy :)

    OdpowiedzUsuń
  4. Tomek, obowiązkowo zajmij się Grailsami para-naukowo, a na pewno nie pożałujesz. Brakuje takiego rzeczowego i poukładanego spojrzenia na Grails/Groovy vs Wicket vs Django vs RoR. Jak starczyłoby Ci pary dodałbym jeszcze JBoss Seam i GWT, bo widać, z ankiety, że one rządzą. Chętnie pomogę, gdzie będzie to możliwe. Należałoby ustawić porównanie z poziomu realizowania przez nie podstawowych funkcjonalności w aplikacjach webowych, np. uwierzytelnianie, warunkowe bezpieczeństwo, pobieranie danych z bazy, wiele zakładek, ładne URLe, captcha, ajax i pewnie kilka innych. Najlepiej wysłać zapytanie o podstawowe cechy na pl.comp.lang.java i do dzieła!

    OdpowiedzUsuń
  5. A może jeszcze język Scala i framework Lift można dorzucić do takiego porównania? :)

    OdpowiedzUsuń
  6. @Tomasz
    Porównanie Grails i frameworków frontendowych nie przekonuje mnie. Grails to pełny framework integraycjny. Front end to kawałek. Grails ma pluginy do Flex, Spring MVC, Wicket, GWT...

    Już bardziej przemawiało by do mnie porównanie między Seam a Grails, jak i Lift, Rails, Django itp. Czyli kompletnymi frameworkami.

    Porównanie samych frontendów może być ciekawe, bardzo różne jak i podobne podejścia, każde pociąga ze sobą wady i zalety. Poza samą dojrzałością konkretnej technologii jest jeszcze jej teoretyczny potencjał.

    @Jacek
    Jak używam Groovy? Skryptowo. Posprzątać, pozamiatać, zrobić mały skrypcik. Podobało mi się ostatnio jak parę dni temu uczelnia zorganizowała karaoke dla erasmusów, ale nie wydrukowała listy piosenek, a te są trzymane gdzieś w hierarchii na dysku. Java na komputerze była więc wystarczyło jeden zip ściągnąć, trochę magii,... I od razu formalności na uczelni szybciej mi się załatwia ;)

    Ponad połowa studentów to informatycy byli, jednak Groovy to czysta magia w odpowiednich przykładach.

    Mnie na początku w Groovy rozbroiło najbardziej:

    def f = new File("plik.txt")
    println f.text

    Niestety zawiodła mnie tu księgarnia i z 4 książek, które chciałem kupić dotarła tylko jedna (EMF), więc Grails czeka do obrony magisterki lub do czasu dorwania dobrej książki.

    Jeszcze do Scali mnie ciągnie, bo statyczna typizacja, silniejszy styl funkcyjny.

    Pozdrawiam,
    Krzysiek

    OdpowiedzUsuń
  7. Co Wy tak z tą Scalą i Liftem?! To zaczyna angażować coraz więcej ludzi. Ciekawy materiał na bloga. Może ktoś się od nas skusi (nas = polskich bloggerów). Byłoby wspaniale! A może pora zajrzeć w stronę Ruby, aby porównać RoR z rozwiązaniami javowymi - w końcu nauka Ruby to nowy język, ale zawsze można wrócić na jvm z jruby, więc nie tak daleko, a całkowicie nowe. Scala wciąż wydaje się być dla mnie egzotycznym rozwiązaniem.

    Jeśli potrzebujesz dobrej książki do Groovy/Grails, napisz do mnie. Wyślę je do Ciebie. Mam kilka w pdfie, więc nawet nie potrzeba poczty "analogowej" angażować.

    OdpowiedzUsuń
  8. W Lift nie wnikałem, ale Scala = statyczna typizacja + odgadywanie typów + styl funkcyjny. Scala jest egzotyczna, bo jest dużo bardziej funkcyjna niż Groovy. Groovy pozwala na styl funkcyjny, Scala go trochę narzuca. Nie wiem czy kojarzysz operacje jak filter, map, foldLeft, reduceLeft. Podejrzewam, że jak ktoś korzysta ze Scala to je kojarzy :)

    Kod wygląda jak w językach dynamicznych, ale jest statycznie typowany, więc po prostu musi być szybszy (w ramach aktualnej wiedzy o językach). Szybkość Scala jest porównywalna z Java, tak jak Ocaml jest porównywalny z C++. No i statyczna typizacja oznacza łatwiejszą refaktoryzacje, lepsze narzędzia, mniej testów itp.

    Silne skupienie na podejściu funkcyjnym i preferowanie zmiennych które są stałe ma ułatwiać wielowątkowość.

    Jeszcze elastyczna składnia i lubiany przeze mnie pattern matching (choć w Ocaml jest ładniejszy).

    To powiedziawszy Scala nie jest przeze mnie używana, a Groovy tak. Groovy ma minimalną krzywą nauki. Korzysta się z tego, z czego się chce korzystać. Jak się zna podstawowe paradygmaty z innych języków to można się konstrukcji domyśleć. Scala zmienia bardzo dużo podstaw.

    http://tinyurl.com/dcfm4c

    Pozdrawiam,
    Krzysztof Kowalczyk

    OdpowiedzUsuń
  9. @Krzysiek
    Aktualnie plan magisterki to porównanie frameworków frontendowych w postaci: JPA (Hibernate) + Spring jako wspólna podstawa, a na froncie: Wicket i 2-3 inne frameworki do porównań. Waham się ciągle z wyborem pozostałych, bo zależałoby mi na dwóch rzeczach: porównaniu kilku różnych filozofii i dodatkowo na poznaniu technologii, które na rynku pracy nie pojawiają się tylko sporadycznie :)

    A co do sensowności porównania Wicket z Grails to myślę, że w miarę ok byłoby porównanie Wicket z JPA i Spring, Groovy/Grails z przyległościami oraz jeszcze czegoś odmiennego: RoR, a może wspomniana Scala z Lift, choć tutaj zaczynam się już zapuszczać w zupełnie nieznane mi obszary :)

    Ogólnie takie porównanie technologii webowych w przekroju bez ograniczania się tylko do tych javowych wydaje się bardzo ciekawym tematem na magisterkę, tylko nie wiem czy z braku czasu (praca, studia, dziewczyna) nie pozostanę przy wersji z frameworkami tylko z javowego poletka :)

    OdpowiedzUsuń
  10. @Tomasz
    Rozważyłbym rozważenie

    - JSF z Facelates np. RichFaces - przykład podejścia komponentowego. Do samego poprawiania JSF jest kilka dodatkowych frameworków. JSF to standard i enterprise-friendly, ale sam standard to "pomyłka" stąd minimum facelets. ewentualnie JSF w wersji z Seam.

    - GWT - jako specyficzna filozofia - przykład silnie Javowego podejścia - enterprise-friendly

    - jakiś przykład MVC mniej zorientowany na kod Java niż Wicket np. Spring MVC

    Hm, Seam mozna wykorzystac z JSF, Wicket i GWT ;)

    Jeszcze rozważyłbym Stripes dlatego, że mniej modne ;)

    Koniecznie JSF i koniecznie GWT. Swoją drogą czemu Wicket?

    Jeśli uczelnia wymaga tak jak u mnie jakiegoś wkładu pisanego zwanego teorią, to potrzebny będzie model oceny lub nawet model jakości do porównania tych pomarańczy. Dorzucenie wtedy Grails albo RoR itp. polecam poważnie przemyśleć bo to porównywanie pomarańczy z jabłonią i wiśnią (a nie z jabłkami i wiśniami !). Da się zrobić ale zasady porównania trzeba poważnie przemyśleć.

    OdpowiedzUsuń
  11. @Krzysiek
    Dzięki za podpowiedzi. Też myślałem o JSF i GWT, a Twoja sugestia utwierdziła mnie w przekonaniu, że to może być dobry kierunek.

    A dlaczego Wicket? Bo chyba się zakochałem ;) Po kiepskich wrażeniach z Tapestry 5, przejście na Wicketa było bardzo orzeźwiające :) Również artykuły właściciela bloga, na którym sobie tak bezczelnie offtopujemy, odegrały swoją rolę :) Inna sprawa, że podczas pisania średniego projektu okazywało się, że wymagania klienta, o których początkowo myślałem, że będą trudne do wykonania, faktycznie są za pomocą Wicketa łatwe do osiągnięcia.

    A co do sposobu porównania to mam kilka pomysłów. Między innymi, tak jak napisał Jacek: "np. uwierzytelnianie, warunkowe bezpieczeństwo, pobieranie danych z bazy, wiele zakładek, ładne URLe, captcha, ajax". Dodatkowo jakiś czas temu wpadł mi w ręce artykuł o analizie frameworków pod kątem wydajności (JMeter) oraz pamięciożerności i myślę, że taki kierunek też byłby wart wzięcia pod uwagę.
    Póki co kończę tworzenie wspólnej warstwy (dao + logika) i zaczynam aplikację w Wicket :)

    OdpowiedzUsuń
  12. Jeśli mam coś porównać, zawsze kojarzy mi się przy okazji "Feature model" Czarneckiego. Podejrzewam, że dało by się zastosować go do klasyfikacji filozofii webframeworków, jak i ich zakresu. Na takim modelu fajnie było by widać skąd taki wybór frameworków. Przykład zastosowania tutaj (ostrzegam, dużo model driven *): http://www.swen.uwaterloo.ca/~kczarnec/ECE750T7/czarnecki_helsen.pdf
    Lepszy przykład był w artykule http://portal.acm.org/citation.cfm?id=1165106 ale nie wiem czy masz dostęp.

    Nie wiem czy da się sensowny model stworzyć dla webframeworków, musiałbym spróbować zrobić by to stwierdzić ;) Mimo to polecam bo dobry model nie jest zły i nawet jeśli nie użyjesz to pokazuje pewne podejście.

    ps. mam nadzieje, że "właściciel bloga" nie jest zły z powodu offtopic

    Pozdrawiam,
    Krzysztof Kowalczyk

    OdpowiedzUsuń
  13. Zły?! Chyba żartujesz. Tylko pokazuje, że brakuje takiego miejsca na dyskusje, prowokowane przez wpisy na blogu :)

    Zastanawiałem się trochę nad tym porównywaniem i faktycznie widzę, że niektóre rozwiązania (szkielety aplikacyjne) nie przystają do siebie, np. Seam jest bardziej obszerny niż Wicket czy GWT. Seam jest na pograniczu "wymagany serwer aplikacyjny Java EE"-"serwer niekonieczny". GWT to szkielet webowy, podobnie Wicket, podczas gdy Seam to szkielet aplikacyjny korzystający z EJB3+JSF. Grails podobnie jak Seam to szkielet aplikacyjny oparty na Spring+Hibrnate. Porównanie Seam vs Grails jest dopasowane w każdym calu - Seam to szkielet aplikacyjny z EJB3+JSF, a Grails z Spring+Hibernate. To są bardzo podobne rozwiązania. Ciekawe, czy to samo można powiedzieć o RoR i Django. Chyba tak, ale nie mam doświadczenia, aby być chociażby w 75% pewien.

    Bardzo kształcący wątek! Oby tak dalej :)

    OdpowiedzUsuń
  14. Z technologiami web w Java to tak jak z kołkami rozporowymi, ludzie myślą że jest tylko jeden rodzaj. Ok, może są lepsze i gorsze... Potem się dziwią czemu kołek do pustaków nie trzyma się w karton-gipsie, a do betonu w pustakach. Nie różnią się wiele z wyglądu, ale różnica w efektywności może przyprawić o ból głowy ;)

    Zresztą różnice między GWT, Wicket i JSF też są spore. GWT pozawala na zmniejszenie przesyłanych danych, korzystanie z czystej Java i daje AJAX. Traci się kontrole nad wyjściem, współpracę z projektantami itp. W JSF trzeba nauczyć się nowego "języka", inaczej działa AJAX i również traci się trochę kontroli nad wyjściem ale ma się ją w jakimś stopni, ale jak bardzo trzeba to można dogadać się z projektantami (co pewnie się kończy korzystaniem z Facelets albo Shale\Clay). JSF dodatkowo obsługuje różne wyjścia (w teorii, bo w praktyce to widziałem tylko Oracle co jest w stanie wypluwać wyjście na konsole/telnet i Seam co renderuje np. maile). JSF wspiera workflow natywnie, a z zabawkami jeszcze lepiej, ale wymusza korzystanie z POST. Wicket daje kontrole nad html, świetny sposób na budowę komponentów, silną kontrole nad logiką bo ta jest w Java itp.

    Reasumując różnice są spore :)

    Szkoda że uczelnie i szkolenia mają tendencje pokazywać budowę kołka, ale przeważnie tylko jednego kołka. I tak dobrze jeśli pokażą jaki do jakiego problemu kołek został stworzony, ale wtedy już nie pokazują zakresu w jakim się nie sprawdza...

    Pozdrawiam,
    Krzysztof Kowalczyk

    OdpowiedzUsuń