Wracając do tematu...
W rozdziale 10. "Reporting" możemy przeczytać jak przyjemnym tematem jest wdrożenie funkcjonalności raportowania w Grails z użyciem otwartego JasperReports. Integracja między Grails a JasperReports będzie niezwykle dynamiczna - korzystamy z już i tak dynamicznego Grails (dzięki Groovy) z wykorzystaniem mechanizmu dynamicznego wywoływania metod (ala mechanizm prześwietlania w Javie - ang. Java Reflection).
Wymagania stawiane rozwiązaniu raportującemu to możliwość tworzenia wielu raportów, w różnych formatach - PDF, HTML, TXT, RTF, XLS, CSV oraz XML - w podejściu DRY (ang. Don't Repeat Yourself - oddzielenie odpowiedzialności i enkapsulacja) na bazie modelu i dynamicznego wywoływania metod w Grails. Pojawia się bardzo skomplikowany diagram powiązań (związków) między poszczególnymi elementami aplikacji z kontrolerami, usługą i widokami. Stworzony zostaje znacznik GSP g:report, który wskazuje na kontroler i jego akcję jako właśnie kontrolera całej interakcji w systemie, co ostatecznie kończy się przesłaniem raportu do użytkownika. Autorzy podkreślają rolę poszczególnych elementów systemu, aby kontroler ReportController jedynie kontrolował przepływ danych między składowymi systemu, w którym pobranie danych o zadaniach użytkownika leży w gestii kolejnego kontrolera TodoController z dynamicznie dołączanymi do klas domenowych metodami bazodanowymi. Usługa ReportService odpowiada za interakcję z JasperReports. Każdy ma swoją rolę w systemie. Jeśli ktokolwiek (przy tak skromnym opisie systemu) zadaje sobie pytanie, dlaczego ReportController przekazuje dane raportowe do raportu zamiast oczekiwać, że to raport będzie odszukiwał konieczne dane za pomocą SQL, spieszę z wyjaśnieniem, że jest to poruszone w książce i sprowadza się do łatwości (a właściwie jej braku) zarządzania SQLem. SQL jest ukryty pod klasami domenowymi w Grails, więc warto skorzystać z gotowego rozwiązania.
JasperReports jest rozwiązaniem raportującym, które składa się ze środowiska uruchmomieniowego JasperReports oraz edytora raportów iReport. Rozpoczynamy od definicji raportu w iReport, w którym możemy umieścić elementy graficzne oraz wykresy, z użyciem XML. Do szablonu raportu dołączane są dane i powstaje raport w formacie PDF, XML, HTML, CSV, XLS, RTF i TXT. Do wyrysowania raportów JasperReports korzysta z dodatkowych bibliotek. Sam motor JasperReports jedynie wiąże szablon ze źródłem danych, parametrami i konfiguracją do eksportera raportowego. Eksporter zwraca ByteArrayOutputSource do aplikacji. W przypadku Grails, wystarczy ustawić odpowiedni rodzaj treści (ang. content type) w odpowiedzi HTML i wysłać odpowiedź do przeglądarki.
JasperReports korzysta z Abstract Window Toolkit (AWT), więc uruchomienie go w niegraficznych środowiskach uniksowych wymaga parametru -Djava.awt.headless=true, np. w zmiennej JAVA_OPTS w Grails.
Integracja JasperReports z aplikacją grailsową rozpoczyna się od przekopiowania koniecznych bibliotek z iReport/lib do katalogu lib aplikacji grailsowej. One z kolei trafią do WEB-INF/lib podczas budowania aplikacji webowej.
Przykładowa aplikacja korzysta ze źródła danych JasperReports opartego na JavaBeans (ang. JavaBeans data source), tj. dane pobierane są z listy klas dziedzinowych przekazywanych do usługi ReportService. Utworzenie definicji raportu w iReport wymaga wskazania na klasy dziedzinowe przez zmienną CLASSPATH, które domyślnie (w Grails 1.0.x) znajdują się w USER_HOME/.grails/1.0.x/projects/<projekt_grailsowy>/classes. Na zakończenie pracy z iReport otrzymujemy szablon raportu jako plik jrxml. Kompilujemy szablon do pliku jasper, który kopiujemy do web-app/reports aplikacji.
Zgodnie z konwencją Grails utworzenie własnego znacznika GSP polega na utworzeniu klasy, której nazwa kończy się TagLib w grails-app/taglib. Znacznik tworzymy poleceniem grails create-tag-lib Report, które tworzy samą klasę znacznika oraz test integracyjny w test/integration. Sam znacznik jest domknięciem (metodą w klasie) z dwoma parametrami - mapą atrybutów znacznika attrs oraz jego zawartością (treścią) body. Zdumiewające jest, że wiele z tych informacji zabrakło w rozdziale poświęconym GSP - rozdział 5. "Building the User Interface" (relacja z lektury w Tworzenie interfejsu użytkownika w Grails - rozdział 5 z "Beginning Groovy and Grails"). W klasie znacznika skorzystano z dostępnego domyślnie (dzięki integracji ze Spring Framework) obiektu grailsAttributes - za jego pomocą możemy odczytać kontekst aplikacji - def appPath = grailsAttributes.getApplicationUri(request) czy out, który jest strumieniem wyjściowym.
Następnie tworzy się kontroler ReportController poleceniem grails create-controller Report. W kontrolerze możemy wykorzystać DI ze Spring Framework i wystarczy zadeklarować klasę usługi ReportService reportService jako pole instancji klasy, aby Grails (poprzez Springa) przekazał (wstrzelił) zależność automatycznie. W klasie kontrolera korzysta się z (Listing 10-4)
ApplicationContext ctx = (ApplicationContext) session.getServletContext()do pobrania (z kontekstu springowego) instancji kontrolera i dynamicznego wykonania metody na niej (Listing 10-4):
.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
def controller = ctx.getBean("${params._controller}")
def inputCollection = controller."${params._action}"(params)Przywyknięcie do tego typu składania i wykonywania metod dynamicznie będzie mnie jeszcze kosztowało nielada wysiłku umysłowego.
Autorzy zwracają uwagę na (potencjalny) pomysł połączenia kontrolera ReportController z usługą ReportService i odradzają go, gdyż "the controller's purpose is to control, not do the actual work". Pamiętajmy o tym - kłania się wzorzec MVC. Tym samym cała wiedza o komunikacji system-JasperReports jest w jednym miejscu - usłudze. Nie jest ona trywialna, gdyż poszczególne formaty raportów wymagają specjalnej obsługi. Stworzenie usługi to wydanie polecenia grails create-service Report.
Na koniec ukłon w stronę alternatywnego rozwiązania - wdrożenia wtyczki grailsowej Jasper, która stała się inspiracją dla całego rozwiązania raportowego w tym rozdziale. Wtyczka opiera się na założeniu, że to raport pobiera dane za pomocą SQL. Instalacja wtyczki to grails install-plugin Jasper.
Brak komentarzy:
Prześlij komentarz