Datei wird geladen, bitte warten...
Zitiervorschau
Tytuł oryginału: Java EE 6 Development with NetBeans 7 Tłumaczenie: Tomasz Walczak ISBN: 978-83-246-8939-2 Copyright © 2011 Packt Publishing First published in the English language under the title ‘Java EE 6 Development with NetBeans’. © Helion 2014. All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie bierze jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Wydawnictwo HELION nie ponosi również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: [email protected] WWW: http://helion.pl (księgarnia internetowa, katalog książek) Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/jave6n.zip Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/jave6n_ebook Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland.
Poleć książkę na Facebook.com
Księgarnia internetowa
Kup w wersji papierowej
Lubię to! » Nasza społeczność
Oceń książkę
Spis treści O autorze
7
O recenzentach
9
Przedmowa
11
Rozdział 1. Wprowadzenie do środowiska NetBeans
15
Wprowadzenie Pobieranie środowiska NetBeans Instalowanie środowiska NetBeans Pierwsze uruchomienie środowiska NetBeans Konfigurowanie środowiska NetBeans pod kątem programowania w Javie EE Instalowanie pierwszej aplikacji Wskazówki dotyczące wydajnego programowania w środowisku NetBeans Podsumowanie
Rozdział 2. Tworzenie aplikacji sieciowych z wykorzystaniem serwletów i technologii JSP Tworzenie pierwszej aplikacji Programowanie serwletów Zabezpieczanie aplikacji sieciowych Fragmenty JSP Podsumowanie
Rozdział 3. Wzbogacanie stron JSP za pomocą biblioteki JSTL i niestandardowych znaczników Podstawowe znaczniki JSTL SQL-owe znaczniki JSTL Modyfikowanie danych w bazie za pomocą znacznika Ostatnie uwagi na temat biblioteki JSTL Niestandardowe znaczniki JSP Podsumowanie
15 16 19 26 27 35 38 49
51 52 72 81 93 96
97 98 107 114 122 122 129
Spis treści
Rozdział 4. Tworzenie aplikacji sieciowych z wykorzystaniem platformy JavaServer Faces 2.0 Wprowadzenie do platformy JSF Tworzenie pierwszej aplikacji JSF Tworzenie szablonów faceletów Komponenty złożone Podsumowanie
Rozdział 5. Tworzenie eleganckich aplikacji sieciowych z wykorzystaniem biblioteki PrimeFaces Pierwszy projekt utworzony z wykorzystaniem biblioteki PrimeFaces Stosowanie komponentów PrimeFaces w aplikacjach JSF Widoki z zakładkami Interfejsy oparte na kreatorze Dodatkowe informacje Podsumowanie
Rozdział 6. Interakcja z bazami danych za pomocą interfejsu Java Persistence API Tworzenie pierwszej encji JPA Automatyczne generowanie encji JPA Relacje między encjami Generowanie aplikacji JSP na podstawie encji JPA Podsumowanie
Rozdział 7. Implementowanie warstwy biznesowej za pomocą ziaren sesyjnych Wprowadzenie do ziaren sesyjnych Tworzenie ziaren sesyjnych w środowisku NetBeans Dostęp do ziarna z poziomu klienta Zarządzanie transakcjami w ziarnach sesyjnych Programowanie aspektowe z wykorzystaniem interceptorów Usługi zegara w ziarnach EJB Generowanie ziaren sesyjnych na podstawie encji JPA Podsumowanie
Rozdział 8. Interfejs API CDI Wprowadzenie do CDI Kwalifikatory Stereotypy Typy do wiązania interceptorów Podsumowanie
4
131 132 132 152 159 164
165 165 169 173 178 183 183
185 186 200 209 215 221
223 224 224 233 238 239 242 244 248
249 249 256 260 263 267
Spis treści
Rozdział 9. Przesyłanie komunikatów za pomocą usług JMS i ziaren sterowanych komunikatami Wprowadzenie do interfejsu JMS Tworzenie projektu i zasobów JMS Przetwarzanie komunikatów JMS za pomocą ziaren sterowanych komunikatami Podsumowanie
Rozdział 10. Usługi sieciowe SOAP oparte na interfejsie JAX-WS Wprowadzenie do usług sieciowych Tworzenie prostej usługi sieciowej Udostępnianie ziaren EJB jako usług sieciowych Podsumowanie
Rozdział 11. Usługi sieciowe RESTful oparte na interfejsie JAX-RS Generowanie usług sieciowych typu RESTful na podstawie istniejącej bazy danych Testowanie usług sieciowych typu RESTful Tworzenie klienta usług sieciowych typu RESTful Podsumowanie
Dodatek A. Debugowanie aplikacji dla firm za pomocą debugera środowiska NetBeans Debugowanie aplikacji dla firm Podsumowanie
Dodatek B. Wykrywanie problemów z wydajnością za pomocą profilera środowiska NetBeans Profilowanie aplikacji Podsumowanie
Skorowidz
269 270 271 280 283
285 285 286 298 305
307 308 314 319 325
327 327 333
335 335 339
341
5
Spis treści
6
1 O autorze David R. Heffelfinger jest dyrektorem ds. technicznych w firmie konsultingowej Ensode Technology, LLC, która działa w Waszyngtonie i okolicach. David zawodowo zajmuje się architekturą, projektowaniem i tworzeniem oprogramowania od 1995 roku. Używa Javy jako głównego języka programowania od 1996 roku. Pracował nad wieloma dużymi projektami dla różnych klientów. Oto niektórzy z nich: IBM, Accenture, Lockheed Martin, Fannie Mae, Freddie Mac, Departament Bezpieczeństwa Krajowego Stanów Zjednoczonych i Departament Obrony Stanów Zjednoczonych. David uzyskał tytuł magistra z zakresu inżynierii oprogramowania na uniwersytecie Southern Methodist. Jest też redaktorem naczelnym serwisu Ensode.net (http://www.ensode.net) poświęconego Javie, Linuksowi i innym zagadnieniom z obszaru technologii. Chcę podziękować wszystkim, którzy przyczynili się do powstania tej książki. Dziękuję redaktorom prowadzącym, Kartikey Pandey i Tariqowi Rakhengemu, oraz koordynatorowi projektu, Shubhanjanowi Chatterjee. Jestem wdzięczny także recenzentom technicznym, Allanowi Bondowi, Arunowi Gupcie i Bruno Vernayowi, za wnikliwe komentarze i sugestie. Ponadto dziękuję zespołowi z firmy Oracle odpowiedzialnemu za NetBeans za opracowanie tak znakomitego środowiska IDE. Dziękuję również mojej żonie i córce za wytrwałość w trakcie długich godzin pracy, które nie pozwalały mi być razem z rodziną.
Java EE 6. Tworzenie aplikacji w NetBeans 7
8
O recenzentach Allan Bond jest programistą i od ponad 10 lat aktywnie działa w branży IT. Zajmuje się głównie tworzeniem systemów za pomocą Javy i pokrewnych technologii. Pracował i udzielał konsultacji w wielu organizacjach — od małych firm po korporacje z listy Fortune 500 i agencje rządowe. Allan uzyskał tytuł magistra z zakresu zarządzania systemami informatycznymi na uniwersytecie Brigham Young. Dziękuję mojej żonie i dzieciom za cierpliwość wykazywaną w noce (i czasem w weekendy), kiedy pracowałem nad zakończeniem recenzji tej książki.
Arun Gupta jest ambasadorem technologii Java EE i GlassFish w firmie Oracle. Ma ponad 15 lat doświadczenia w branży tworzenia oprogramowania. W tym czasie pracował z platformą Java™ i różnymi technologiami sieciowymi. Obecnie zajmuje się budowaniem i rozwijaniem społeczności związanej z technologiami Java EE i GlassFish. Pracuje w zespole odpowiedzialnym za Javę EE od momentu powstania tej platformy i przyczynił się do udostępnienia wszystkich jej wersji. Wygłaszał prelekcje na różne tematy na całym świecie. Uwielbia angażować się w kontakty ze społecznością, klientami, partnerami i członkami grup użytkowników Javy, aby edukować ich z zakresu zalet tej technologii. Jest aktywnym blogerem prowadzącym bloga http://blogs.oracle.com/arungupta. Zamieścił tam ponad 1200 wpisów, a ich czytelnikami są osoby z całego świata (łączna liczba wizyt na blogu przekracza 1,2 miliona). Arun jest też zapalonym biegaczem — jest gotowy do biegu w każdym zakątku świata. Możesz skontaktować się z nim na kanale @arungupta. Bruno Vernay zajmował się tworzeniem baz danych i aplikacji sieciowych, sieciami i zabezpieczeniami, silnikami obsługi komunikatów i reguł, eksploracją danych, portalami, mechanizmami pojedynczego logowania i procesami łączenia systemów nazw, a wszystko to w kontekście Javy, Linuksa i oprogramowania o otwartym dostępie do kodu źródłowego. Bruno od ponad 13 lat wciąż szuka nowych wyzwań. Interesują go „przygody ludzkości”, angażuje się w życie
Java EE 6. Tworzenie aplikacji w NetBeans 7
społeczności globalnie i lokalnie, a także jest członkiem grupy AlpesJUG.FR. Chętnie i łatwo opanowuje nowe technologie i równie łatwo rezygnuje z przestarzałych rozwiązań. Stara się spędzać czas na poznawaniu nowych rzeczy dzięki lekturze książek. Lubi pomagać innym.
10
Przedmowa W Javie EE 6 (najnowszej wersji specyfikacji Java EE) pojawiło się kilka nowych funkcji upraszczających tworzenie aplikacji dla firm. W najnowszej edycji Javy EE znalazły się nowe wersje istniejących interfejsów API. Interfejs JSF 2.0 znacznie ułatwia tworzenie aplikacji sieciowych, a JPE 2.0 zawiera nowy interfejs API do obsługi kryteriów oraz kilka innych usprawnień. Ziarna sesyjne EJB wzbogacono o obsługę asynchronicznych wywołań metod i o inne mechanizmy. W specyfikacji Servlet 3.0 pojawiło się kilka nowych technik, np. dodatkowe wywołania metod i możliwość ustawienia deskryptora web.xml jako opcjonalnego. Ponadto w Javie EE znalazło się kilka nowych interfejsów API. Są to np. JAX-RS, który upraszcza tworzenie usług sieciowych typu REST, oraz CDI, pomagający integrować różne warstwy typowych aplikacji dla firm. Środowisko NetBeans zostało zaktualizowane pod kątem obsługi wszystkich mechanizmów Javy EE 6. Dzięki temu tworzenie aplikacji w Javie EE 6 jest teraz jeszcze szybsze i łatwiejsze. Dzięki tej książce poznasz wszystkie mechanizmy środowiska NetBeans, dzięki którym praca nad aplikacjami dla firm pisanymi w Javie EE 6 stanie się bardzo prosta.
Zawartość książki Rozdział 1., „Wprowadzenie do środowiska NetBeans”. Tu znajdziesz wprowadzenie do środowiska NetBeans, wskazówki pomagające przyspieszyć pracę oraz sztuczki pozwalające na wydajniejsze tworzenie aplikacji w Javie. Rozdział 2., „Tworzenie aplikacji sieciowych z wykorzystaniem serwletów i technologii JSP”. Z tego rozdziału dowiesz się, w jaki sposób środowisko NetBeans wspomaga tworzenie aplikacji sieciowych opartych na interfejsie API serwletów i technologii JavaServer Pages.
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rozdział 3., „Wzbogacanie stron JSP za pomocą biblioteki JSTL i niestandardowych znaczników”. W tym rozdziale opisano, w jaki sposób środowisko NetBeans pomaga w tworzeniu łatwych w konserwacji aplikacji sieciowych z wykorzystaniem biblioteki JSTL (ang. JavaServer Pages Standard Tag Library). Dowiesz się tu także, jak pisać niestandardowe znaczniki JSP. Rozdział 4., „Tworzenie aplikacji sieciowych z wykorzystaniem platformy JavaServer Faces 2.0”. W tym rozdziale wyjaśniono, w jaki sposób środowisko NetBeans pomaga w łatwym tworzeniu aplikacji sieciowych z wykorzystaniem platformy JavaServer Faces 2.0. Rozdział 5., „Tworzenie eleganckich aplikacji sieciowych z wykorzystaniem biblioteki PrimeFaces”. Z tego rozdziału dowiesz się, jak za pomocą biblioteki komponentów JSF PrimeFaces tworzyć eleganckie aplikacje sieciowe z kompletną obsługą Ajaksa. Rozdział 6., „Interakcja z bazami danych za pomocą interfejsu Java Persistence API”. Tu opisano, w jaki sposób środowisko NetBeans umożliwia łatwe tworzenie aplikacji wykorzystujących interfejs JPA (ang. Java Persistence API). Dowiesz się tu, jak automatycznie generować encje JPA na podstawie istniejących schematów. W rozdziale tym wyjaśniono też, jak za pomocą kilku kliknięć tworzyć kompletne aplikacje sieciowe na podstawie schematów istniejących baz danych. Rozdział 7., „Implementowanie warstwy biznesowej za pomocą ziaren sesyjnych”. Z tego rozdziału dowiesz się, w jaki sposób środowisko NetBeans ułatwia tworzenie ziaren sesyjnych EJB 3.1. Rozdział 8., „Interfejs API CDI”. W tym rozdziale wyjaśniono, jak nowy interfejs API CDI (ang. Contexts and Dependency Injection) wprowadzony w Javie EE 6 pomaga w integrowaniu różnych warstw aplikacji. Rozdział 9., „Przesyłanie komunikatów za pomocą usług JMS i ziaren sterowanych komunikatami”. Tu opisano technologie przesyłania komunikatów z Javy EE, m.in. JMS (ang. Java Messaging Service) i MDB (ang. Message Driven Beans). Poznasz tu mechanizmy środowiska NetBeans upraszczające tworzenie aplikacji dzięki wykorzystaniu interfejsów API do przesyłania komunikatów. Rozdział 10., „Usługi sieciowe SOAP oparte na interfejsie JAX-WS”. W tym rozdziale wyjaśniono, w jaki sposób środowisko NetBeans pomaga w łatwym tworzeniu usług SOAP opartych na interfejsie JAX-WS (ang. Java API for XML Web Services). Rozdział 11., „Usługi sieciowe RESTful oparte na interfejsie JAX-RS”. Tu opisano interfejs JAXRS — nowy element specyfikacji Java EE upraszczający tworzenie usług sieciowych REST. Dodatek A, „Debugowanie aplikacji dla firm za pomocą debugera środowiska NetBeans”. Tu znajdziesz wprowadzenie do debugera środowiska NetBeans i dowiesz się, jak używać go do wyszukiwania usterek w aplikacjach. Dodatek B, „Wykrywanie problemów z wydajnością za pomocą profilera środowiska NetBeans”. Tu opisano profiler środowiska NetBeans i wyjaśniono, jak używać go do analizowania problemów z wydajnością aplikacji.
12
Przedmowa
Czego potrzebujesz do pracy z tą książką? Potrzebny jest pakiet Java Development Kit (JDK) w wersji 1.6 lub nowszej oraz środowisko NetBeans 7.0 Java EE Edition.
Dla kogo przeznaczona jest ta książka? Książka ta przeznaczona jest dla trzech grup programistów: dla programistów Javy (niekoniecznie używających środowiska NetBeans), którzy chcą
nauczyć się biegle używać Javy EE 6 i stosować środowiska NetBeans do tworzenia aplikacji w Javie EE; dla użytkowników środowiska NetBeans chcących się dowiedzieć, jak używać tego
środowiska do tworzenia aplikacji w Javie EE 6; doświadczonych programistów Javy EE 6, którzy chcą się dowiedzieć, w jaki sposób
środowisko NetBeans ułatwi im tworzenie aplikacji opartych na tej platformie.
Konwencje W tej książce zauważysz wiele stylów tekstu, które pozwalają odróżniać od siebie różne rodzaje informacji. Oto przykłady zastosowania tych stylów wraz z objaśnieniami. W tekście fragmenty kodu są wyróżnione w następujący sposób: „Programista skopiował znacznik i wkleił go w fragmencie strony JSP”. Bloki kodu mają następującą postać:
Logowanie
Jeśli pewien fragment kodu ma przykuwać Twoją uwagę, odpowiednie wiersze lub elementy są wyróżnione pogrubieniem:
Podaj nazwę użytkownika i hasło, aby uzyskać dostęp do aplikacji
13
Java EE 6. Tworzenie aplikacji w NetBeans 7
Nowe pojęcia i ważne słowa są wyróżnione kursywą. Słowa, które pojawiają się na ekranie (np. w menu lub oknach dialogowych), są zapisywane w tekście tak: „Aby utworzyć fragment JSP w środowisku NetBeans, kliknij File/New File i wybierz kategorię Web”. Wskazówki, sugestie i ważne informacje pojawiać się będą w takich ramkach.
Informacje od czytelników Informacje od czytelników zawsze są mile widziane. Daj znać, co myślisz o książce — co Ci się podobało, a co nie. Informacje te są ważne dla wydawnictwa, ponieważ pomagają w pisaniu coraz lepszych książek. Aby przesłać ogólne informacje, wyślij e-mail pod adresem
[email protected] (w temacie wiadomości wpisz tytuł książki). Jeśli potrzebujesz książki na określony temat i chcesz, abyśmy ją opublikowali, wypełnij i wyślij formularz SUGGEST A TITLE ze strony www.packtpub.com lub prześlij e-mail pod adresem
[email protected]. Jeżeli jesteś ekspertem z określonej dziedziny i chcesz napisać książkę lub zostać współautorem, zapoznaj się ze wskazówkami dla autorów na stronie www.packtpub.com/authors.
14
1 Wprowadzenie do środowiska NetBeans W tym rozdziale opisano, jak zacząć pracę w środowisku NetBeans. Przedstawiono tu następujące zagadnienia: wprowadzenie, pobieranie środowiska NetBeans, instalowanie środowiska NetBeans, pierwsze uruchamianie środowiska NetBeans, konfigurowanie środowiska NetBeans pod kątem programowania w Javie EE, instalowanie pierwszej aplikacji, wskazówki dotyczące wydajnego programowania w środowisku NetBeans.
Wprowadzenie NetBeans to zintegrowane środowisko programistyczne (ang. Integrated Development Environment — IDE) i platforma. Choć początkowo środowisko NetBeans służyło tylko do pisania aplikacji w Javie, w wersji 6. pojawiła się obsługa kilku innych języków programowania (wbudowana lub wymagająca zainstalowania dodatkowych wtyczek). Języki programowania z wbudowaną obsługą w środowisku NetBeans to: Java, JavaFX, C, C++ i PHP. Za pomocą dodatkowych wtyczek można zapewnić obsługę języków Groovy, Scala, Ruby i innych. NetBeans to nie tylko środowisko IDE, ale też platforma. Programiści mogą za pomocą interfejsów API NetBeans tworzyć wtyczki dla tego środowiska i niezależne aplikacje.
Java EE 6. Tworzenie aplikacji w NetBeans 7
Krótką historię środowiska NetBeans znajdziesz na stronie http://netbeans.org/about/history.html.
Choć środowisko NetBeans obsługuje kilka języków programowania, to z uwagi na to, że początkowo służyło wyłącznie do pisania programów w Javie, jest najbardziej popularne wśród programistów tego języka. NetBeans ma wbudowaną obsługę aplikacji tworzonych w Javie SE (wersja Standard Edition), uruchamianych zwykle na komputerach biurkowych i laptopach, aplikacji rozwijanych w Javie ME (wersja Micro Edition), działających zazwyczaj w małych urządzeniach, np. w telefonach komórkowych i palmtopach, a także aplikacji pisanych w Javie EE (wersja Enterprise Edition), które zazwyczaj działają na dużych serwerach i jednocześnie obsługują tysiące użytkowników. Głównym tematem tej książki są funkcje środowiska NetBeans przeznaczone do programowania w Javie EE, a także sposoby wykorzystania tych funkcji do wydajniejszego tworzenia aplikacji w tej wersji Javy. Omawiane mechanizmy związane są np. z przyspieszaniem tworzenia aplikacji sieciowych za pomocą platformy JSF lub interfejsu API serwletów oraz technologii JSP — środowisko NetBeans udostępnia punkt wyjścia do tworzenia potrzebnych elementów. Dowiesz się też, jak używać palety narzędzi środowiska NetBeans do przeciągania fragmentów kodu (w tym znaczników HTML i JSP) do stron JSP, a także jak za pomocą środowiska NetBeans generować encje JPA na podstawie schematów istniejących baz danych (JPA — ang. Java Persistence API — to standardowe narzędzie do tworzenia odwzorowań obiektowo-relacyjnych dostępne w Javie EE). Oprócz zgłębiania zagadnień związanych z tworzeniem aplikacji sieciowych, dowiesz się też, w jaki sposób w środowisku NetBeans łatwo tworzyć ziarna EJB i usługi sieciowe. W książce opisano również, jak łatwo pisać klienty ziaren EJB i usług sieciowych, wykorzystując wygodne mechanizmy środowiska NetBeans. Zanim jednak zaczniesz korzystać z wszystkich funkcji środowiska NetBeans, musisz je zainstalować. Opis tego procesu znajdziesz w następnym podrozdziale.
Pobieranie środowiska NetBeans Środowisko NetBeans można pobrać ze strony http://www.netbeans.org (rysunek 1.1). Aby pobrać środowisko NetBeans, trzeba kliknąć przycisk z etykietą Download (numer nad przyciskiem odpowiada aktualnej wersji środowiska, dlatego się zmienia). Gdy klikniesz ten przycisk, przejdziesz do strony z listą wszystkich pakietów środowiska NetBeans dostępnych do pobrania (rysunek 1.2).
16
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.1. Witryna poświęcona środowisku NetBeans
Rysunek 1.2. Można pobrać różne wersje środowiska NetBeans
17
Java EE 6. Tworzenie aplikacji w NetBeans 7
Można pobrać różne pakiety środowiska NetBeans, oferujące odmienne funkcje. W tabeli 1.1 opisano różne dostępne pakiety środowiska i dostępne w nich funkcje. Tabela 1.1. Dostępne pakiety środowiska NetBeans Pakiet środowiska NetBeans
Opis
Java SE
Umożliwia tworzenie aplikacji w Javie przeznaczonych na komputery biurkowe.
Java EE
Umożliwia tworzenie aplikacji w Javie SE (zwykle aplikacji na komputery biurkowe) i Javie EE (działających na dużych serwerach aplikacji dla firm).
C/C++
Umożliwia tworzenie aplikacji w językach C lub C++.
PHP
Umożliwia tworzenie aplikacji sieciowych za pomocą popularnego języka o otwartym dostępie do kodu źródłowego PHP.
All
Obejmuje funkcje z wszystkich pakietów środowiska NetBeans.
Aby móc wykonywać przykłady z tej książki, wybierz pakiet Java EE lub All. Zrzuty ekranowe zamieszczone w tej książce wykonano za pomocą pakietu Java EE. Jeśli pobierzesz pakiet All, środowisko NetBeans może wyglądać nieco inaczej (dostępne będą dodatkowe opcje menu).
Oficjalnie obsługiwane są następujące platformy: Windows 7, Vista, XP i 2000, Linux x86, Linux x64, Solaris x86, Solaris x64, Mac OS X. Środowisko NetBeans można ponadto uruchomić na dowolnej platformie z Javą 6 lub nowszą. Aby pobrać wersję środowiska przeznaczoną dla takich platform, wybierz wersję niezależną od systemu operacyjnego. Choć wersję środowiska NetBeans niezależną od systemu operacyjnego można uruchamiać na dowolnej z obsługiwanych platform, najlepiej pobrać wersję przeznaczoną dla używanego systemu.
Strona pobierania środowiska NetBeans powinna wykryć używany system operacyjny i domyślnie wybrać odpowiednią platformę. Jeśli tak się nie stanie (lub jeżeli pobierasz środowisko NetBeans w celu zainstalowania go w komputerze z innym systemem), wybierz właściwą platformę z listy rozwijanej o trafnej nazwie Platform. Po wybraniu odpowiedniej platformy kliknij przycisk Download przy wybranym pakiecie NetBeans. Na potrzeby programowania w Javie EE należy wybrać pakiet Java EE lub All. Wtedy przeglądarka pobierze pakiet NetBeans do wybranego katalogu.
18
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Aplikacje w Javie EE trzeba instalować na serwerze aplikacji. Na rynku istnieje kilka takich serwerów, a pakiety Java EE i All obejmują serwery GlassFish i Tomcat. Tomcat to popularny kontener serwletów o otwartym dostępie do kodu źródłowego. Można go używać do instalowania aplikacji opartych na serwletach i technologiach JSP oraz JSF, jednak nie obsługuje innych technologii Javy EE (np. ziaren EJB ani interfejsu JPA). GlassFish to serwer aplikacji w pełni zgodny z Javą EE. Do instalowania i uruchamiania przykładów z tej książki posłuży właśnie ten serwer.
Instalowanie środowiska NetBeans Aby można było zainstalować środowisko NetBeans, dostępny musi być pakiet JDK (ang. Java Development Kit) w wersji 6.0 lub nowszej. Ponieważ książka jest przeznaczona dla doświadczonych programistów Javy, nie poświęcono tu dużo miejsca na wyjaśnianie procesu instalowania i konfigurowania pakietu JDK, ponieważ można z dużą dozą pewności przyjąć, że docelowi odbiorcy książki prawdopodobnie mają już zainstalowane to narzędzie. Instrukcje dotyczące instalowania pakietu JDK 6 znajdziesz na stronie http://www.oracle.com/technetwork/ java/javase/index-137561.html. Użytkownicy systemu Mac OS X znajdą instrukcje instalacji i przeznaczony dla tej platformy pakiet JDK pod adresem http://developer.apple.com/java/.
Proces instalowania środowiska NetBeans różni się nieco w zależności od platformy. W kilku kolejnych punktach opisano, jak zainstalować to środowisko w każdej z obsługiwanych platform.
Microsoft Windows W systemach z rodziny Microsoft Windows środowisko NetBeans jest pobierane jako plik wykonywalny o nazwie netbeans-7.0-ml-java-windows.exe lub podobnej (nazwa zależy od wybranej wersji i pakietu pobieranego środowiska NetBeans). Aby zainstalować środowisko NetBeans w systemach Windows, wystarczy przejść do katalogu z pobranym pakietem i dwukrotnie kliknąć plik wykonywalny.
Mac OS X W systemie Mac OS X pobrany plik nosi nazwę netbeans-7.0-ml-java-macosx.dmg lub podobną (nazwa zależy od wybranej wersji i pakietu pobieranego środowiska NetBeans). Aby zainstalować środowisko NetBeans, wystarczy przejść do katalogu z pobranym pakietem i dwukrotnie kliknąć plik wykonywalny.
19
Java EE 6. Tworzenie aplikacji w NetBeans 7
Instalator dla systemu Mac OS X obejmuje cztery pakiety: NetBeans, GlassFish, Tomcat i OpenESB. Trzeba zainstalować osobno każdy z nich. Aby zainstalować poszczególne pakiety, należy je dwukrotnie kliknąć. Warto zauważyć, że pakiet GlassFish trzeba zainstalować przed pakietem OpenESB.
Linux i Solaris W systemach Linux i Solaris środowisko NetBeans jest pobierane w postaci skryptu powłoki. Nazwa pliku jest podobna do netbeans-7.0-ml-java-linux.sh, netbeans-7.0-ml-java-solaris-x86.sh lub netbeans-7.0-ml-java-solaris-sparc.sh, i zależy od wybranych wersji środowiska NetBeans, pakietu i platformy. Przed zainstalowaniem środowiska NetBeans w tych platformach trzeba przekształcić pobrany plik w wersję modyfikowalną. Można to zrobić z poziomu wiersza poleceń. Należy przejść do katalogu, do którego pobrałeś instalator środowiska NetBeans, a następnie wywołać następujące polecenie: chmod +x ./nazwa_pliku.sh
Człon nazwa_pliku.sh należy zastąpić nazwą pliku odpowiednią dla platformy i pakietu NetBeans. Po utworzeniu pliku wykonywalnego środowisko można zainstalować z poziomu wiersza poleceń: ./filename.sh
Także tu człon nazwa_pliku.sh należy zastąpić nazwą pliku odpowiednią dla platformy i pakietu NetBeans.
Inne platformy Na potrzeby innych platform środowisko NetBeans można pobrać jako plik .zip niezależny od systemu. Nazwa tego pliku jest podobna do netbeans-7.0-201007282301-mljava.zip (zależy to od pobieranej wersji i pakietu NetBeans). Aby zainstalować środowisko NetBeans w takich platformach, wystarczy wypakować plik .zip do odpowiedniego katalogu.
Proces instalacji Choć proces uruchamiania instalatora przebiega odmiennie na poszczególnych platformach, sam instalator w większości z nich działa podobnie.
20
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Wyjątkiem jest tu instalator dla systemu Mac OS X. W tym systemie każdy komponent (NetBeans, GlassFish, Tomcat i OpenESB) ma odrębny instalator i wymaga niezależnej instalacji. Ponadto pakiet GlassFish trzeba zainstalować przed pakietem OpenESB. Innym wyjątkiem jest plik .zip niezależny od systemu. Plik ten nie zawiera instalatora, a zainstalowanie tej wersji środowiska NetBeans polega na wypakowaniu pliku .zip do odpowiedniego katalogu.
Po uruchomieniu przeznaczonego dla używanej platformy pliku instalacyjnego środowiska NetBeans pojawia się okno podobne do tego z rysunku 1.3.
Rysunek 1.3. Pierwszy ekran instalatora
Wyświetlane pakiety zależą od pobranego pakietu środowiska NetBeans. Powyższy ekran odpowiada pakietowi dla Javy EE. Na tym etapie należy kliknąć przycisk Next>, aby kontynuować proces instalacji. Środowisko NetBeans jest udostępniane na dwóch licencjach: GPL (ang. GNU Public License) w wersji 2., z wyjątkiem CLASSPATH i CDDL (ang. Common Development and Distribution License). Obie licencje są zatwierdzone przez grupę OSI (ang. Open Source Initiative). Aby kontynuować instalowanie środowiska NetBeans, zaznacz pole wyboru I accept the terms in the license agreement i kliknij przycisk Next> (rysunek 1.4).
21
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.4. Ekran z licencją
Na tym etapie można albo zaakceptować warunki licencji platformy JUnit, albo zrezygnować z instalowania tego narzędzia. Następnie instalator wyświetla prośbę o określenie katalogu instalacyjnego środowiska NetBeans i pakietu JDK używanego dla tego środowiska (rysunek 1.5). Można wpisać nowe wartości lub zastosować ustawienia domyślne. Po wybraniu odpowiedniego katalogu instalacyjnego i pakietu JDK należy kliknąć przycisk Next>, aby kontynuować instalację. Środowisko NetBeans używa zmiennej środowiskowej JAVA_HOME do zapełnienia pola z katalogiem pakietu JDK.
Następnie instalator wyświetla pole, w którym należy podać nazwę katalogu instalacyjnego serwera aplikacji GlassFish (rysunek 1.6). Można podać katalog lub zastosować ustawienia domyślne. W następnym kroku instalator wymaga podania katalogu instalacyjnego Tomcata — popularnego kontenera serwletów udostępnianego razem ze środowiskiem NetBeans (rysunek 1.7).
22
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.5. Ustawianie katalogu instalacyjnego i pakietu JDK używanego dla środowiska NetBeans
Rysunek 1.6. Pole, w którym należy podać nazwę katalogu instalacyjnego serwera GlassFish
23
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.7. Pole, w którym należy podać nazwę katalogu instalacyjnego Tomcata
Na tym etapie instalator wyświetla podsumowanie wybranych ustawień (rysunek 1.8). Po przejrzeniu podsumowania należy kliknąć przycisk Install, aby rozpocząć instalowanie.
Rysunek 1.8. Podsumowanie ustawień
24
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rozpocznie się proces instalacji. Instalator wyświetla pasek postępu z informacjami o postępie instalacji (rysunek 1.9).
Rysunek 1.9. Instalator informuje o postępie instalacji
Po zainstalowaniu środowiska NetBeans i wszystkich powiązanych komponentów instalator informuje o udanym zakończeniu pracy oraz umożliwia wyrażenie zgody na anonimowe przesyłanie danych o użytkowaniu środowiska (rysunek 1.10). Po wybraniu odpowiednich ustawień wystarczy kliknąć przycisk Finish, aby zakończyć działanie instalatora.
Rysunek 1.10. Końcowy ekran instalatora
25
Java EE 6. Tworzenie aplikacji w NetBeans 7
W większości platform instalator umieszcza na pulpicie ikonę środowiska NetBeans. Powinna ona wyglądać tak jak na rysunku 1.11.
Rysunek 1.11. Ikona środowiska NetBeans
Pierwsze uruchomienie środowiska NetBeans Środowisko NetBeans można uruchomić, dwukrotnie klikając jego ikonę. W trakcie rozruchu powinien pojawić się ekran startowy środowiska (rysunek 1.12).
Rysunek 1.12. Ekran startowy środowiska NetBeans
Po uruchomieniu środowiska NetBeans powinieneś zobaczyć stronę z odnośnikami do programów demonstracyjnych, samouczków, przykładowych projektów itd. (rysunek 1.13). Środowisko NetBeans domyślnie wyświetla stronę startową przy każdym uruchomieniu. Jeśli nie chcesz, aby środowisko za każdym razem automatycznie pokazywało tę stronę, możesz ją wyłączyć. W tym celu usuń zaznaczenie pola Show on Startup w dolnej części strony. Stronę startową zawsze możesz wyświetlić za pomocą opcji Help/Start Page.
26
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.13. Strona z odnośnikami do pomocnych materiałów
Konfigurowanie środowiska NetBeans pod kątem programowania w Javie EE Środowisko NetBeans jest wstępnie skonfigurowane tak, aby można było korzystać z serwera aplikacji GlassFish 3 i systemu RDBMS JavaDB. Jeśli chcesz używać tych narzędzi, nie musisz robić nic więcej, aby skonfigurować środowisko. Można jednak zintegrować środowisko NetBeans z innymi serwerami aplikacji dla Javy EE (takimi jak JBoss, Weblogic lub WebSphere) lub systemami RDBMS (takimi jak MySQL, PostgreSQL, Oracle lub dowolny inny system zgodny z interfejsem JDBC, co w praktyce oznacza niemal dowolny system RDBMS).
Integrowanie środowiska NetBeans z niezależnym serwerem aplikacji Integrowanie środowiska NetBeans z serwerem aplikacji jest bardzo proste. Poniżej opisano czynności, jakie trzeba wykonać.
27
Java EE 6. Tworzenie aplikacji w NetBeans 7
Z tego punktu dowiesz się, jak zintegrować środowisko NetBeans z serwerem JBoss. Proces integracji przebiega bardzo podobnie także w przypadku innych serwerów aplikacji i kontenerów serwletów.
1. Najpierw wybierz opcję Window/Services (rysunek 1.14).
Rysunek 1.14. Wybierz opcję Services
2. Następnie kliknij prawym przyciskiem myszy węzeł Servers w drzewie w oknie Services i wybierz opcję Add Server… z menu podręcznego (rysunek 1.15).
Rysunek 1.15. Wybierz opcję Add Server…
3. W oknie, które się pojawi, wybierz z listy serwer do zainstalowania, a następnie kliknij przycisk Next> (rysunek 1.16). 4. Potem należy wskazać miejsce instalacji serwera w systemie plików (rysunek 1.17) i kliknąć przycisk Next>. 5. W ostatnim kroku trzeba określić domenę, host i port serwera aplikacji (rysunek 1.18). Następnie można kliknąć przycisk Finish. W oknie Services powinien pojawić się nowo dodany serwer aplikacji (rysunek 1.19). I to już wszystko! Zintegrowałeś środowisko NetBeans z niezależnym serwerem aplikacji.
28
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.16. Wybierz serwer, który chcesz zainstalować
Rysunek 1.17. Wskaż miejsce instalacji serwera
29
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.18. Ustaw właściwości serwera
Rysunek 1.19. Dodany serwer jest widoczny w oknie Services
Integrowanie środowiska NetBeans z niezależnym systemem RDBMS Środowisko NetBeans jest domyślnie zintegrowane z systemem RDBMS JavaDB. Ponadto posiada sterowniki JDBC dla innych systemów tego typu, takich jak MySQL i PostgreSQL. Dostępny jest też sterownik pomostowy JDBC-ODBC pozwalający łączyć się z systemami RDBMS, które nie mają wbudowanej obsługi interfejsu JDBC lub dla których trudno uzyskać sterownik JDBC.
30
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Choć używanie sterownika JDBC-ODBC pozwala łączyć się z większością systemów RDBMS bez konieczności korzystania ze sterownika JDBC, zwykle lepiej zdobyć sterownik JDBC dostosowany do konkretnego systemu. Sterownik JDBC-ODBC nie zapewnia optymalnej wydajności, a ponadto dostępne są sterowniki JDBC dla większości systemów RDBMS. W tym punkcie dowiesz się, jak nawiązać połączenie z narzędziem HSQLDB — napisanym w Javie systemem RDBMS o otwartym dostępie do kodu źródłowego. Celem jest tu zilustrowanie integrowania środowiska NetBeans z niezależnymi systemami RDBMS. Proces ten przebiega podobnie także w przypadku innych systemów tego rodzaju, np. Oracle, Sybase, SQL Server itd.
Dodawanie sterownika JDBC do środowiska NetBeans Przed połączeniem się z niezależnym systemem RDBMS trzeba dodać do środowiska NetBeans sterownik JDBC przeznaczony dla tego systemu. Aby dodać sterownik JDBC, należy kliknąć prawym przyciskiem myszy węzeł Database/Drivers w zakładce Services (rysunek 1.20).
Rysunek 1.20. Kliknij węzeł Drivers prawym przyciskiem myszy
Następnie należy wybrać plik JAR ze sterownikiem JDBC dla używanego systemu RDBMS. Środowisko NetBeans odgaduje nazwę klasy ze sterownikiem JDBC (rysunek 1.21). Jeśli w pliku JAR znajduje się kilka klas sterowników, poprawną można wybrać z listy rozwijanej Driver Class. Następnie wystarczy kliknąć przycisk OK, aby dodać sterownik do środowiska NetBeans. Po wykonaniu opisanych czynności nowy sterownik JDBC pojawi się na liście zarejestrowanych sterowników (rysunek 1.22).
31
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.21. Wskazywanie pliku ze sterownikiem JDBC
Rysunek 1.22. Nowy sterownik jest dostępny na liście
Łączenie się z niezależnym systemem RDBMS Po dodaniu do środowiska NetBeans sterownika JDBC dla używanego systemu RDBMS można połączyć się z niezależnym systemem tego typu. Aby nawiązać połączenie z niezależnym systemem RDBMS, należy kliknąć prawym przyciskiem myszy odpowiedni sterownik w zakładce Services, a następnie wybrać opcję Connect Using… z menu podręcznego (rysunek 1.23).
32
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.23. Wybierz opcję Connect Using…
Potem trzeba wprowadzić adres URL sterownika JDBC oraz nazwę użytkownika i hasło dla bazy danych (rysunek 1.24).
Rysunek 1.24. Wpisz dane potrzebne do nawiązania połączenia
33
Java EE 6. Tworzenie aplikacji w NetBeans 7
Po kliknięciu przycisku Next> środowisko NetBeans może wyświetlić prośbę o wybranie schematu bazy danych (rysunek 1.25).
Rysunek 1.25. Wybierz schemat bazy danych
Gdy wybierzesz schemat i klikniesz przycisk OK, dodana baza danych pojawi się na liście baz w oknie Services (rysunek 1.26). Aby się z nią połączyć, należy kliknąć jej nazwę prawym przyciskiem myszy, wybrać opcję Connect z menu podręcznego, a następnie wprowadzić nazwę użytkownika i hasło dla bazy danych (ponieważ przy dodawaniu bazy ustalono, że środowisko NetBeans nie będzie zapamiętywać hasła).
Rysunek 1.26. Łączenie się z dodaną bazą danych
34
Rozdział 1. • Wprowadzenie do środowiska NetBeans
W ten sposób udało się z poziomu środowiska NetBeans z powodzeniem połączyć z niezależnym systemem RDBMS.
Instalowanie pierwszej aplikacji W środowisku NetBeans wbudowanych jest kilka przykładowych aplikacji. Aby mieć pewność, że środowisko jest poprawnie skonfigurowane, warto zainstalować jedną z nich w zintegrowanym serwerze aplikacji GlassFish dołączonym do środowiska NetBeans. Aby otworzyć przykładowy projekt, należy wybrać opcję File/New Project, a następnie pozycję Samples/Java Web z listy kategorii w wyświetlonym oknie dialogowym. Gdy na liście kategorii wybierzesz pozycję Java Web, w panelu Projects pojawi się lista projektów. Tu wybierz projekt Servlet Stateless (rysunek 1.27). Jest to prosty projekt, w którym wykorzystano serwlet i bezstanowe ziarno sesyjne. Dzięki temu można zapoznać się z procedurą używania kontenera serwletów GlassFish i kontenera EJB.
Rysunek 1.27. Wybierz projekt Servlet Stateless
Gdy klikniesz przycisk Next>, pojawi się prośba o wprowadzenie nazwy projektu i lokalizacji (rysunek 1.28). Wartości domyślne są odpowiednie.
35
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.28. Możesz pozostawić domyślną nazwę i lokalizację projektu
Po kliknięciu przycisk Finish nowy projekt pojawi się w oknie Projects (rysunek 1.29).
Rysunek 1.29. Nowy projekt w oknie Projects
Można w jednym kroku skompilować, spakować i zainstalować projekt. W tym celu należy kliknąć projekt prawym przyciskiem myszy i wybrać opcję Run z wyświetlonego menu podręcznego (rysunek 1.30). Wtedy powinny pojawić się dane wyjściowe ze skryptu budowania (rysunek 1.31). Ponadto zintegrowany serwer aplikacji GlassFish i zintegrowany system RDBMS JavaDB powinny automatycznie rozpocząć pracę.
36
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.30. Wybierz opcję Run z menu podręcznego
Rysunek 1.31. Informacje od skryptu budowania
Bezpośrednio po zainstalowaniu aplikacji pojawi się nowe okno lub nowa zakładka przeglądarki z domyślną stroną przykładowej aplikacji (rysunek 1.32). Zainstalowana właśnie przykładowa aplikacja sieciowa to prosty program, który ilustruje ciekawą funkcję wprowadzoną w Javie EE 6 — możliwość dodawania zależności do bezstanowych ziaren sesyjnych bez konieczności implementowania interfejsu biznesowego dla tego ziarna (co było konieczne w Javie EE 5) i bez konieczności używania macierzystego interfejsu do pobierania egzemplarza ziarna sesyjnego (co trzeba było robić podczas używania platformy J2EE). Jeśli przeglądarka wyświetla stronę podobną do pokazanej na rysunku 1.32, można mieć pewność, że środowisko NetBeans i serwer GlassFish działają poprawnie. Można więc przystąpić do tworzenia własnych aplikacji w Javie EE.
37
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.32. Domyślna strona zainstalowanej aplikacji
Wskazówki dotyczące wydajnego programowania w środowisku NetBeans Choć środowisko NetBeans udostępnia wiele funkcji, które ułatwiają i przyspieszają programowanie w Javie EE, dostępne są też liczne inne mechanizmy upraszczające pisanie programów w Javie. W kilku kolejnych punktach znajdziesz przegląd najbardziej przydatnych rozwiązań z tego obszaru.
Uzupełnianie kodu Edytor kodu środowiska NetBeans udostępnia bardzo sprawny mechanizm uzupełniania kodu. Na przykład gdy chcesz utworzyć zmienną prywatną, nie musisz wpisywać całego słowa „private”. Wystarczy wprowadzić trzy pierwsze litery („pri”), a następnie wcisnąć kombinację klawiszy Ctrl+Spacja, a środowisko automatycznie uzupełni słowo „private”. Uzupełnianie kodu działa też m.in. dla typów zmiennych i wartości zwracanych przez metody. Jeśli chcesz zadeklarować zmienną typu java.util.List, wystarczy, że wpiszesz kilka pierwszych znaków i wciśniesz kombinację Ctrl+Spacja (rysunek 1.33). Środowisko spróbuje wtedy uzupełnić nazwę na podstawie typów z pakietów zaimportowanych do klasy. Aby przy uzupełnianiu nazw środowisko używało wszystkich typów ze ścieżki CLASSPATH, należy ponownie wcisnąć kombinację Ctrl+Spacja.
38
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.33. Środowisko NetBeans udostępnia wygodny mechanizm uzupełniania kodu
Jak widać na rysunku 1.33, środowisko NetBeans wyświetla dokumentację JavaDoc dla klasy wybranej z opcji mechanizmu uzupełniania kodu. Innym przyspieszającym pracę rozwiązaniem jest to, że klasa wybrana w opcjach jest automatycznie importowana do kodu. Po ustaleniu typu zmiennej można ponownie wcisnąć kombinację Ctrl+Spacja, a środowisko NetBeans zaproponuje nazwę zmiennej (rysunek 1.34).
Rysunek 1.34. Środowisko NetBeans proponuje nazwy zmiennych
39
Java EE 6. Tworzenie aplikacji w NetBeans 7
Gdy chcesz zainicjować zmienną nową wartością, możesz ponownie zastosować skrót Ctrl+Spacja. Pojawi się wtedy lista poprawnych typów (rysunek 1.35). Będą one dostępne jako opcje mechanizmu uzupełniania kodu.
Rysunek 1.35. Lista dozwolonych typów wartości zmiennej
W pokazanym kodzie użyty typ (java.util.List) to interfejs, dlatego mechanizm uzupełniania kodu pozwala wybrać dowolną klasę z implementacją tego interfejsu. Gdyby użyty typ był klasą, mechanizm uzupełniania wyświetliłby zarówno samą klasę, jak i jej podklasy. Gdy chcesz zastosować zmienną, wystarczy, że wpiszesz kilka pierwszych znaków jej nazwy, a następnie wciśniesz kombinację Ctrl+Spacja (rysunek 1.36).
40
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.36. Uzupełnianie nazwy zmiennej
Aby wywołać metodę obiektu, wystarczy wpisać kropkę po nazwie zmiennej, a mechanizm uzupełniania kodu wyświetli wszystkie dostępne metody jako opcje (rysunek 1.37). Zauważ, że automatycznie wyświetlana jest dokumentacja JavaDoc dla wybranej metody.
Szablony kodu Szablony kodu to skróty dla często używanych fragmentów kodu. Aby zastosować szablon kodu, wystarczy wpisać go w edytorze, a następnie wcisnąć klawisz Tab. Środowisko rozwinie wtedy skrót do odpowiadającego mu kompletnego fragmentu kodu. Na przykład gdy wpiszesz sout i wciśniesz klawisz Tab, środowisko rozwinie wpisane słowo do formy System.out.println("");, a kursor zostanie umieszczony między cudzysłowami. Kilka najbardziej przydatnych szablonów znajdziesz w tabeli 1.2. Pamiętaj, że w szablonach kodu wielkość znaków jest istotna.
41
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.37. Wybieranie metody za pomocą mechanizmu uzupełniania kodu Tabela 1.2. Przydatne szablony kodu Skrót
Przykładowe rozwinięcie
Opis
Psf
public static final
Przydatny przy deklarowaniu publicznych zmiennych statycznych z modyfikatorem final.
forc
for (Iterator it = list.
Używa standardowej pętli for do przechodzenia po elementach kolekcji.
iterator(); it.hasNext();) { Object object = it.next(); } fore
for (Object object : list) { }
42
Używa zaawansowanej pętli for do przechodzenia po elementach kolekcji.
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Tabela 1.2. Przydatne szablony kodu — ciąg dalszy Skrót
Przykładowe rozwinięcie
Opis
Ifelse
if (boolVar) {
Generuje instrukcję warunkową if-else.
} else { } psvm
public static void main(String[]
Generuje metodę main klasy.
args) { } soutv
System.out.println("boolVar = " +
Generuje instrukcję System.out.println() wyświetlającą wartość zmiennej.
boolVar); trycatch
try {
Generuje blok try-catch.
} catch (Exception exception) { } whileit
while (iterator.hasNext()) { Object object = iterator.
Generuje pętlę while do przechodzenia po iteratorze.
next(); }
Aby wyświetlić kompletną listę szablonów kodu, kliknij opcję Tools/Options, wybierz ikonę Editor, a następnie otwórz zakładkę Code Templates (rysunek 1.38). Można też dodawać własne szablony. W tym celu należy kliknąć przycisk New. Pojawi się wtedy prośba o podanie skrótu dla szablonu. Po wprowadzeniu skrótu nowy szablon zostanie dodany do listy szablonów i będzie automatycznie wybrany. Wtedy w zakładce Expanded Text można wpisać rozwinięcie szablonu. Warto wspomnieć, że szablony kodu są dostępne nie tylko dla Javy, ale też dla języków HTML i CSS. Można z nich korzystać także w innych edytorach środowiska NetBeans.
Skróty klawiaturowe Środowisko NetBeans udostępnia kilka skrótów klawiaturowych, które umożliwiają bardzo szybkie przeskakiwanie między plikami z kodem źródłowym. Gdy zapamiętasz te skróty, będziesz mógł tworzyć kod znacznie wydajniej, niż używając samej myszy. Wybrane spośród najbardziej przydatnych skrótów klawiaturowych ze środowiska NetBeans opisano w tym punkcie, jednak przedstawiona tu lista nie jest kompletna. Pełną listę takich skrótów znajdziesz w internecie pod adresem http://netbeans.org/projects/www/downloads/download/shortcuts.pdf. Przydatnym skrótem klawiaturowym umożliwiającym szybkie poruszanie się po dużych plikach Javy jest kombinacja Ctrl+F12. Ten skrót powoduje wyświetlenie w edytorze szkieletu bieżącego pliku Javy z metodami i zmiennymi (rysunek 1.39).
43
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.38. Lista szablonów kodu
Rysunek 1.39. Główne elementy pliku Javy
44
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Gdy wpiszesz tekst w polu Filter, lista wyświetlanych zmiennych i metod zostanie odpowiednio przefiltrowana. Wspomniany skrót klawiaturowy pozwala bardzo szybko poruszać się po dużych plikach. Wciśnięcie kombinacji Alt+F12 powoduje wyświetlenie okna dialogowego z hierarchią klas obejmującą bieżącą klasę Javy (rysunek 1.40).
Rysunek 1.40. Hierarchia bieżącej klasy
Skrót ten można wykorzystać do szybkiego przejścia do nadklasy lub podklasy klasy bieżącej. Innym przydatnym skrótem klawiaturowym jest kombinacja Alt+Insert. Można ją wykorzystać do generowania często używanych elementów kodu (np. konstruktorów, getterów, setterów itd.), co przedstawia rysunek 1.41.
Rysunek 1.41. Kombinacja Alt+Insert umożliwia generowanie często używanych fragmentów kodu
Kod jest generowany w miejscu, w którym znajduje się kursor. Ponadto jeśli kursor znajduje się obok otwierającego lub zamykającego nawiasu, wciśnięcie kombinacji Ctrl+[ spowoduje przeniesienie kursora do drugiego nawiasu z pary. Ten skrót działa dla nawiasów klamrowych, zwykłych i kwadratowych. Kombinacja Ctrl+Shift+[ działa podobnie, jednak oprócz przeniesienia kursora do powiązanego nawiasu powoduje też zaznaczenie kodu pomiędzy nawiasami (rysunek 1.42).
45
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.42. Efekt wciśnięcia kombinacji Ctrl+Shift+[
Czasem programista chce znaleźć w projekcie wszystkie miejsca, w których wywoływana jest dana metoda. Wciśnięcie kombinacji Alt+F7 po zaznaczeniu metody pozwala łatwo znaleźć takie informacje (rysunek 1.43).
Rysunek 1.43. Wyszukiwanie wywołań metody za pomocą kombinacji Alt+F7
Skrót ten pozwala także wyszukiwać zmienne. Środowisko NetBeans informuje o błędach kompilacji, podkreślając nieprawidłowy wiersz czerwoną falowaną linią. Gdy umieścisz kursor w takim wierszu i wciśniesz kombinację Alt+Enter, będziesz mógł wybrać jedną z sugestii, aby naprawić kod (rysunek 1.44).
Rysunek 1.44. Naprawianie kodu za pomocą kombinacji Alt+Enter
Czasem poruszanie się po plikach projektu jest niewygodne (zwłaszcza jeśli znasz nazwę pliku, ale nie jesteś pewien, gdzie on się znajduje). Na szczęście środowisko NetBeans udostępnia skrót klawiaturowy Shift+Alt+O, który pozwala szybko otworzyć dowolny plik projektu (rysunek 1.45). Istnieją też inne przydatne skróty klawiaturowe. Shift+Alt+F służy do szybkiego formatowania kodu, a Ctrl+E pozwala skasować bieżący wiersz znacznie szybciej niż przez zaznaczenie wiersza i wciśnięcie klawisza Backspace. Czasem programista importuje klasę do kodu, a później stwierdza, że jej nie potrzebuje. Ponadto niektóre osoby kasują wiersze, w których używana jest dana klasa, ale później zapominają usunąć wiersz importu z początku pliku z kodem źródłowym.
46
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Rysunek 1.45. Szybkie wyszukiwanie plików projektu za pomocą kombinacji Shift+Alt+O
Środowisko NetBeans w obu sytuacjach generuje ostrzeżenie dotyczące nieużywanego importowanego kodu. Wtedy można wcisnąć kombinację Ctrl+Shift+I, aby w jednej operacji usunąć wszystkie nieużywane zaimportowane elementy. Kombinacja ta powoduje też, że środowisko próbuje dodać brakujące instrukcje importu. Ostatnia kwestia, o której warto wspomnieć, nie jest ściśle związana ze skrótami klawiaturowymi. Chodzi o bardzo przydatną funkcję edytora w środowisku NetBeans. Jeśli przytrzymując klawisz Ctrl, klikniesz metodę lub zmienną, dany element zostanie przekształcony w odnośnik. Kliknięcie tego odnośnika spowoduje przejście w środowisku NetBeans do deklaracji danej metody lub zmiennej.
Wizualne wskazówki w środowisku NetBeans Oprócz skrótów klawiaturowych, szablonów kodu i mechanizmu uzupełniania kodu, środowisko NetBeans udostępnia wiele wskazówek wizualnych, które ułatwiają szybkie zrozumienie kodu. Niektóre z najbardziej przydatnych wskazówek przedstawia rysunek 1.46. Jeśli z kodem powiązane jest ostrzeżenie, środowisko NetBeans informuje o tym na dwa sposoby: podkreśla problematyczny wiersz żółtą falistą linią i umieszcza pokazaną na rysunku 1.47 ikonę na lewym marginesie obok tego wiersza. Ikona żółtej żarówki oznacza, że środowisko NetBeans może zaproponować sugestie naprawienia problemu. Wtedy należy przenieść kursor do problematycznego wiersza i wcisnąć opisaną wcześniej kombinację Alt+Enter, aby środowisko NetBeans wyświetliło jedną lub więcej możliwości naprawienia usterki.
47
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 1.46. Przydatne wskazówki wizualne w środowisku NetBeans
Rysunek 1.47. Ikona informująca o ostrzeżeniu
Jeśli wystąpił błąd kompilacji, środowisko NetBeans podkreśla problematyczny wiersz czerwoną falistą linią i umieszcza ikonę z rysunku 1.48 na lewym marginesie obok tego wiersza.
Rysunek 1.48. Ta ikona oznacza błąd kompilacji
Także tu ikona żarówki informuje, że środowisko NetBeans ma sugestie dotyczące rozwiązania problemu. Gdy wciśniesz kombinację Alt+Enter po przeniesieniu kursora do problematycznego wiersza, zobaczysz propozycje środowiska NetBeans. Nie wszystkie wizualne wskazówki w środowisku NetBeans są związane z błędami w kodzie. Dostępne są też inne informacje. Na przykład umieszczenie kursora obok otwierającego lub zamykającego nawiasu powoduje wyróżnienie obu nawiasów z pary (jak w metodzie doSomething() na rysunku 1.46). Jeśli dana metoda przesłania metodę z nadklasy, na lewym marginesie obok deklaracji metody pojawia się ikona z rysunku 1.49.
Rysunek 1.49. Ta ikona oznacza, że dana metoda przesłania metodę z nadklasy
48
Rozdział 1. • Wprowadzenie do środowiska NetBeans
Ta ikona to wielka litera „O” w kółku (O pochodzi tu od angielskiego słowa override, czyli „przesłaniać”). Podobnie — jeśli dana metoda jest implementacją jednego z interfejsów implementowanych w klasie — na lewym marginesie obok deklaracji tej metody pojawia się ikona z rysunku 1.50.
Rysunek 1.50. Ta ikona oznacza, że dana metoda jest implementacją interfejsu
Ta ikona to wielka litera „I” w kółku (I pochodzi tu od angielskiego słowa implements, czyli „implementuje”). Środowisko NetBeans udostępnia też wizualne wskazówki za pomocą różnych czcionek i kolorów. Na przykład metody i zmienne statyczne są wyróżnione kursywą, zmienne składowe są wyświetlane kolorem zielonym, a słowa zarezerwowane Javy mają kolor niebieski. Wszystkie te wskazówki są widoczne na rysunku 1.46 z początkowej części tego punktu. Inną wygodną cechą edytora w środowisku NetBeans jest to, że zaznaczenie metody lub zmiennej powoduje wyróżnienie wszystkich wystąpień tego elementu w otwartym pliku.
Podsumowanie W tym rozdziale przedstawiono krótką historię Javy EE, a także wyjaśniono, jak pobrać i zainstalować środowisko NetBeans dla różnych obsługiwanych platform. Ponadto opisano, jak dla środowiska NetBeans skonfigurować niezależne serwery aplikacji Javy EE i niezależne systemy RDBMS. Dowiedziałeś się m.in., jak zarejestrować sterownik JDBC dla wybranego systemu RDBMS. Zobaczyłeś też, jak utworzyć i zainstalować pierwszą aplikację Javy EE. Wykorzystałeś przy tym jeden z przykładowych projektów dołączonych do środowiska NetBeans. W końcowej części rozdziału poznałeś wybrane mechanizmy środowiska NetBeans: uzupełnianie kodu, szablony kodu, skróty klawiaturowe oraz wizualne wskazówki, które pozwalają wydajniej tworzyć oprogramowanie.
49
Java EE 6. Tworzenie aplikacji w NetBeans 7
50
2 Tworzenie aplikacji sieciowych z wykorzystaniem serwletów i technologii JSP Z tego rozdziału dowiesz się, jak tworzyć aplikacje sieciowe w Javie EE z wykorzystaniem interfejsu API Servlet. Zobaczysz też, jak tworzyć strony JSP (ang. Java Server Pages), aby lepiej rozdzielić warstwę biznesową programu od warstwy prezentacji. Oto zagadnienia omówione w tym rozdziale: tworzenie stron JSP w celu dynamicznego wyświetlania materiałów w sieci; tworzenie serwletów do przetwarzania informacji po stronie serwera w aplikacjach sieciowych Javy; zabezpieczanie aplikacji sieciowych; umieszczanie często używanych znaczników we fragmentach JSP.
Java EE 6. Tworzenie aplikacji w NetBeans 7
Tworzenie pierwszej aplikacji W środowisku NetBeans do tworzenia aplikacji sieciowych służy kategoria Java Web. Aby utworzyć nową aplikację sieciową, należy przygotować nowy projekt. Wybierz opcję File/New Project (lub wciśnij kombinację Ctrl+Shift+N), a następnie otwórz kategorię Java Web i kliknij pozycję Web Application w panelu Projects (rysunek 2.1).
Rysunek 2.1. Tworzenie nowej aplikacji sieciowej
Po kliknięciu przycisku Next> należy wybrać nazwę projektu (rysunek 2.2). W trakcie jej wpisywania środowisko automatycznie modyfikuje lokalizację, katalog i ścieżkę projektu. Choć można zmienić wartości domyślne tych ustawień, zwykle nie warto tego robić, gdyż ułatwiają one konserwowanie aplikacji z uwagi na to, że programiści znający środowisko NetBeans będą znali owe wartości bez konieczności ich sprawdzania. Kliknięcie przycisku Next> powoduje przejście do następnej strony kreatora nowych aplikacji sieciowych (rysunek 2.3).
52
Rozdział 2. • Tworzenie aplikacji sieciowych
Rysunek 2.2. Wpisz nazwę projektu
Rysunek 2.3. Tu możesz wybrać serwer i skonfigurować inne ustawienia
53
Java EE 6. Tworzenie aplikacji w NetBeans 7
Na tym ekranie należy wybrać serwer aplikacji, a także używaną wersję Javy EE i ścieżkę kontekstową aplikacji (podstawowy adres URL). Zwykle wartości domyślne są odpowiednie. W większości przykładów z tej książki używany jest serwer aplikacji GlassFish. Środowisko NetBeans 7.0 jest udostępniane z serwerami GlassFish i Tomcat. Na rysunku 2.3 z listy rozwijanej Server wybrano opcję GlassFish.
Przycisk Next> powoduje przejście do następnej strony kreatora (rysunek 2.4).
Rysunek 2.4. Wybierz potrzebne platformy
Tworzenie aplikacji sieciowych tylko za pomocą serwletów i stron JSP zwykle wymaga ręcznego pisania wielu powtarzających się fragmentów kodu. Dlatego przez lata opracowano wiele platform do tworzenia aplikacji sieciowych, aby zautomatyzować generowanie powtarzalnych elementów. JSF (ang. JavaServer Faces) to standardowa platforma do tworzenia aplikacji sieciowych w Javie EE. Szczegółowe omówienie tej platformy znajdziesz w rozdziale 4. W omawianej aplikacji nie będziesz używał platformy, dlatego kliknij przycisk Finish, aby utworzyć projekt. W tym momencie środowisko NetBeans generuje prostą, ale kompletną aplikację sieciową Javy. Nowy projekt zawiera jedną stronę JSP, którą środowisko NetBeans automatycznie otwiera w edytorze. Ponieważ projekt to kompletna aplikacja Javy, można natychmiast go za-
54
Rozdział 2. • Tworzenie aplikacji sieciowych
instalować. W tym celu należy kliknąć nazwę projektu prawym przyciskiem myszy i wybrać opcję Run z menu podręcznego (rysunek 2.5).
Rysunek 2.5. Uruchamianie projektu
W tym momencie środowisko automatycznie uruchamia zintegrowany serwer aplikacji GlassFish i zintegrowany relacyjny system baz danych JavaDB. W oknie na dane wyjściowe środowisko NetBeans tworzy zakładki dla obu tych narzędzi, a także dodatkową zakładkę dla procesu budowania potrzebnego do uruchomienia projektu (rysunek 2.6). W zakładce Java DB pojawia się zawartość dziennika bazy danych, natomiast w zakładce GlassFish widoczna jest zawartość dziennika serwera aplikacji. Zakładka procesu budowania zawiera dane wyjściowe skryptu budowania wygenerowanego przez środowisko NetBeans dla aplikacji.
Rysunek 2.6. Zakładki dla narzędzi używanych przy uruchamianiu nowej aplikacji
Po kilku sekundach aplikacja zostanie zainstalowana. Na tym etapie otwierana jest domyślna przeglądarka internetowa, w której pojawia się plik JSP projektu (rysunek 2.7).
Rysunek 2.7. Strona JSP nowej aplikacji
55
Java EE 6. Tworzenie aplikacji w NetBeans 7
Wygenerowany plik JSP jest bardzo prosty. Gdy wyświetlisz jego kod źródłowy, zobaczysz, że zawiera niemal wyłącznie standardowe znaczniki HTML:
JSP Page
Hello World!
Znaczniki zawierają komentarze dla stron JSP, dlatego kompilator JSP pomija cały tekst umieszczony między tymi znacznikami. Komentarze te nie są dodawane do strony. Ponadto można stosować standardowe komentarze HTML, umieszczane w znacznikach
|
|
|
|
Pozostały kod generuje tabelę HTML z polami na dane wejściowe i listą (z uwagi na prostotę i zwięzłość obsługiwane są wyłącznie polskie adresy, a na liście rozwijanej dostępne są opcje odpowiadające tylko wybranym województwom).
126
Rozdział 3. • Wzbogacanie stron JSP za pomocą biblioteki JSTL
Na stronie JSP wywołującej nowy znacznik trzeba za pomocą dyrektywy taglib dołączyć utworzoną bibliotekę znaczników:
Używana tu biblioteka niestandardowych znaczników obejmuje wszystkie tego typu znaczniki umieszczone w katalogu WEB-INF/tags aplikacji sieciowej. Aby wskazać własne niestandardowe znaczniki, wykorzystano tu atrybut tagdir dyrektywy taglib w celu wskazania miejsca przechowywania znaczników. Wszystkie własne znaczniki muszą znajdować się bezpośrednio w katalogu WEB-INF/tags lub w jednym z jego podkatalogów. Biblioteka niestandardowych znaczników obejmuje wszystkie tego rodzaju znaczniki z danego katalogu.
Znacznik można wywołać, umieszczając w pliku JSP następujący kod:
W poniższym kodzie strony JSP pokazano, jak używać niestandardowego znacznika:
Strona JSP
|
127
Java EE 6. Tworzenie aplikacji w NetBeans 7
|
Kod z tej strony JSP tworzy egzemplarz ziarna Address i ustawia wartość wybranych właściwości. Następnie ziarno jest ustawiane jako atrybut sesji, dzięki czemu w niestandardowym znaczniku można je pobrać, tak jak wcześniej, za pomocą znacznika . W kodzie strony JSP znajduje się formularz używający niestandardowego znacznika oraz dodatkowy przycisk przesyłania. Gdy uruchomisz projekt, zobaczysz, w jaki sposób niestandardowy znacznik jest wyświetlany w przeglądarce (rysunek 3.32).
Rysunek 3.32. Niestandardowy znacznik wyświetlony w przeglądarce
Wszystkie pola obsługujące dane wejściowe w formularzu zostały wygenerowane za pomocą niestandardowego znacznika. Niestandardowe znaczniki JSP mogą mieć ciało (w przykładowym znaczniku go nie ma). Wtedy kod JSP z wywołaniem znacznika powinien wyglądać tak:
Witajcie!
Jeśli znacznik obejmuje akcje , jego ciało trzeba umieścić między znacznikami i .
W ciele znacznika można umieścić dowolny kod HTML lub JSP. Aby wyświetlić ciało znacznika, należy umieścić akcję w miejscu, gdzie ciało ma być widoczne.
128
Rozdział 3. • Wzbogacanie stron JSP za pomocą biblioteki JSTL
Podsumowanie Z lektury tego rozdziału dowiedziałeś się, jak używać graficznych narzędzi środowiska NetBeans do dodawania znaczników JSTL do stron JSP. Zobaczyłeś, w jaki sposób znaczniki JSTL pozwalają wzbogacić możliwości stron JSP, a jednocześnie zwiększyć ich czytelność dzięki ograniczeniu stosowania skryptletów. Ponadto nauczyłeś się tworzyć niestandardowe znaczniki JSP, aby ukryć kod i funkcje JSP. Zobaczyłeś również, że środowisko NetBeans generuje początkowy plik znacznika, który można wykorzystać jako punkt wyjścia do tworzenia własnych niestandardowych znaczników.
129
Java EE 6. Tworzenie aplikacji w NetBeans 7
130
4 Tworzenie aplikacji sieciowych z wykorzystaniem platformy JavaServer Faces 2.0 W dwóch poprzednich rozdziałach wyjaśniono, jak tworzyć aplikacje sieciowe w Javie za pomocą serwletów i stron JSP. Choć za pomocą tych interfejsów API zbudowanych zostało wiele starszych aplikacji, większość współczesnych aplikacji sieciowych opartych na Javie tworzy się za pomocą jednej z przeznaczonych do tego platform. Standardową platformą do tworzenia aplikacji sieciowych jest JavaServer Faces (JSF). W tym rozdziale zobaczysz, w jaki sposób zastosowanie tej platformy pozwala uprościć budowanie aplikacji. W tym rozdziale opisano następujące zagadnienia: Tworzenie projektu JSF w środowisku NetBeans. Rozmieszczanie znaczników JSF za pomocą znacznika . Używanie nawigacji statycznej i dynamicznej do definiowania mechanizmów
poruszania się między stronami. Używanie w środowisku NetBeans kreatora New JSF Managed Bean do tworzenia
zarządzanych ziaren JSF.
Java EE 6. Tworzenie aplikacji w NetBeans 7
Implementowanie niestandardowych walidatorów w JSF. Łatwe generowanie szablonów JSF 2.0 za pomocą kreatorów ze środowiska NetBeans. Łatwe tworzenie komponentów złożonych JSF 2.0 za pomocą środowiska NetBeans.
Wprowadzenie do platformy JSF Przed pojawieniem się platformy JSF większość aplikacji sieciowych w Javie programiści tworzyli za pomocą niestandardowych platform (takich jak Apache Struts, Tapestry, Spring Web MVC itd.). Platformy te są oparte na serwletach i stronach JSP oraz automatyzują generowanie wielu mechanizmów, które przy bezpośrednim stosowaniu tych interfejsów API trzeba tworzyć ręcznie. Dostępność wielu platform do tworzenia aplikacji sieciowych (w czasie, gdy powstawała ta książka, w Wikipedii wymienionych było 31 tego typu platform da Javy, a lista ta nie była kompletna!) często prowadziła do paraliżu decyzyjnego. Programiści nieraz poświęcali bardzo dużo czasu na ocenę platform w kontekście potrzebnych zastosowań. Powstanie zgodnej z Javą EE platformy JSF spowodowało, że pojawiła się standardowa platforma do tworzenia aplikacji sieciowych dla serwerów aplikacji zgodnych z tą specyfikacją Javy. Nie chcę przez to powiedzieć, że inne platformy do tworzenia aplikacji sieciowych są przestarzałe i że w ogóle nie warto z nich korzystać. Jednak wiele firm uznaje platformę JSF za bezpieczny wybór, ponieważ jest częścią standardu i w przewidywalnej przyszłości powinno być dostępne dla niej dobre wsparcie techniczne. Ponadto środowisko NetBeans zapewnia doskonałą obsługę platformy JSF, dlatego jest ona bardzo atrakcyjnym wyborem.
JSF sama w sobie nie jest platformą do tworzenia aplikacji sieciowych — jest to platforma komponentów. Teoretycznie można przy jej użyciu pisać aplikacje inne niż sieciowe, jednak w praktyce wykorzystuje się ją niemal wyłącznie do budowania rozwiązań sieciowych. Oprócz tego, że jest standardową platformą komponentów Javy EE, JSF ma też inną zaletę, a mianowicie zapewnia dobrą obsługę dla producentów narzędzi. Dzięki temu w środowisku NetBeans można wykorzystać model komponentów platformy JSF i umożliwić przeciąganie ich na strony.
Tworzenie pierwszej aplikacji JSF Z perspektywy programisty aplikacja JSF składa się ze zbioru stron XHTML z niestandardowymi znacznikami JSF, jednego lub kilku zarządzanych ziaren JSF i opcjonalnego pliku konfiguracyjnego faces-config.xml. 132
Rozdział 4. • Tworzenie aplikacji sieciowych
W wersji JSF 1.x plik faces-config.xml był wymagany, jednak w wersji JSF 2.0 wprowadzono pewne konwencje, dzięki którym konfiguracja jest potrzebna w mniejszym stopniu. Ponadto wiele aspektów konfiguracji JSF można ustawić za pomocą adnotacji. Zmniejsza to, a w niektórych sytuacjach całkowicie eliminuje konieczność stosowania wspomnianego XML-owego pliku konfiguracyjnego.
Tworzenie nowego projektu JSF Aby utworzyć nowy projekt JSF, należy wybrać opcję File/New Project, otworzyć kategorię projektów Java Web i wybrać typ projektu Web Application. Po kliknięciu przycisku Next> podaj nazwę projektu i, opcjonalnie, zmień inne informacje na jego temat (rysunek 4.1). Wartości domyślne ustawione przez środowisko NetBeans są zwykle sensowne.
Rysunek 4.1. Określanie nazwy i lokalizacji nowego projektu
Na następnej stronie kreatora można wybrać serwer, wersję Javy EE i ścieżkę kontekstu aplikacji (rysunek 4.2). Tu pozostaw wartości domyślne. Na kolejnej stronie kreatora nowego projektu można wybrać platformy używane w tworzonej aplikacji sieciowej (rysunek 4.3).
133
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 4.2. Określanie serwera, wersji Javy i ścieżki dla projektu
Rysunek 4.3. Wybieranie platformy dla nowej aplikacji sieciowej
134
Rozdział 4. • Tworzenie aplikacji sieciowych
Nie jest zaskoczeniem, że w aplikacji JSF trzeba wybrać platformę JavaServer Faces. Gdy klikniesz przycisk Finish, kreator wygeneruje szkielet projektu JSF. Obejmuje on jeden plik faceletu, index.xhtml, i plik konfiguracyjny web.xml (rysunek 4.4).
Rysunek 4.4. Wygenerowany szkielet projektu JSF
Plik web.xml to standardowy opcjonalny plik konfiguracyjny potrzebny w aplikacjach sieciowych Javy. Od wersji 3.0 interfejsu API Servlet (wersję tę wprowadzono w Javie EE 6) plik ten jest opcjonalny. W wielu sytuacjach plik web.xml nie jest już potrzebny, ponieważ większość opcji konfiguracyjnych można ustawić za pomocą adnotacji. Jednak w aplikacjach JSF warto utworzyć taki plik, ponieważ pozwala określić etap projektu JSF.
javax.faces.PROJECT_STAGE Development
Faces Servlet javax.faces.webapp.FacesServlet 1
Faces Servlet /faces/*
30
faces/index.xhtml
135
Java EE 6. Tworzenie aplikacji w NetBeans 7
Jak widać, środowisko NetBeans automatycznie ustawia etap projektu JSF na Development (czyli na wersję rozwojową). Powoduje to skonfigurowanie platformy JSF w taki sposób, aby udostępniała dodatkowe narzędzia diagnostyczne, niedostępne na innych etapach. Na przykład częstym problemem w trakcie tworzenia aplikacji jest to, że w czasie rozwijania strony sprawdzanie poprawności niektórych pól kończy się niepowodzeniem, przy czym programista nie dodał do strony znaczników lub (więcej na ten temat już za chwilę). Gdy w takiej sytuacji formularz zostanie przesłany, będzie się wydawać, że strona nie wykonuje żadnych operacji lub że nawigacja nie działa. Gdy etap projektu jest ustawiony na Development, komunikaty o błędach z zakresu sprawdzania poprawności są automatycznie dodawane do strony. Programista nie musi dzięki temu dodawać wspomnianych znaczników do strony (oczywiście należy je dodać przed utworzeniem wersji produkcyjnej kodu, ponieważ użytkownicy nie będą widzieli automatycznie generowanych komunikatów o błędach z zakresu sprawdzania poprawności). Poniżej znajdziesz dozwolone dla faceletów wartości parametru kontekstowego javax.faces. PROJECT_STAGES: Development, Production, SystemTest, UnitTest. Wcześniej wspomniano, że wybranie etapu Development powoduje dodanie informacji diagnostycznych ułatwiających programowanie. Dwie pozostałe poprawne wartości związane z tworzeniem aplikacji (SystemTest i UnitTest) umożliwiają zaimplementowanie niestandardowych operacji dla tych dwóch faz. W klasie javax.faces.application.Application dostępna jest metoda getProjectStage(), która umożliwia określenie etapu projektu. Dzięki temu można napisać kod uruchamiany tylko na odpowiednim etapie. Takie rozwiązanie przedstawiono w poniższym fragmencie kodu: public void someMethod() { FacesContext facesContext = FacesContext.getCurrentInstance(); Application application = facesContext.getApplication(); ProjectStage projectStage = application.getProjectStage(); if (projectStage.equals(ProjectStage.Development)) { // Kod związany z pisaniem aplikacji } else if (projectStage.equals(ProjectStage.Production)) { // Kod wersji produkcyjnej } else if (projectStage.equals(ProjectStage.SystemTest)) { // Kod związany z testami systemu } else if (projectStage.equals(ProjectStage.UnitTest)) { // Kod związany z testami jednostkowymi } }
Jak pokazuje ten fragment, można napisać kod wykonywany na dowolnym etapie projektu na podstawie wartości zwróconej przez metodę getProjectStage() klasy Application.
136
Rozdział 4. • Tworzenie aplikacji sieciowych
Gdy tworzysz sieciowy projekt w Javie za pomocą platformy JSF, automatycznie generowany jest facelet. Wygenerowany facelet wygląda tak:
Tytuł faceletu
Powitania od faceletu
Jak widać, facelet to plik XHTML z charakterystycznymi dla faceletów przestrzeniami nazw XML. Na przedstawionej wcześniej automatycznie wygenerowanej stronie poniższa definicja przestrzeni nazw pozwala używać biblioteki komponentów JSF „h” (litera ta oznacza „HTML”): xmlns:h="http://java.sun.com/jsf/html"
Dzięki tej deklaracji można używać znaczników charakterystycznych dla JSF, np. i , które zastępują standardowe znaczniki HTML i XHTML oraz . Aplikacja wygenerowana przez kreator nowych projektów jest prostą, ale kompletną aplikacją sieciową JSF. Aby zobaczyć, jak działa, kliknij projekt prawym przyciskiem myszy w oknie projektów i wybierz opcję Run. Spowoduje to uruchomienie serwera aplikacji (jeśli jeszcze nie działa), zainstalowanie aplikacji i otwarcie domyślnej przeglądarki systemowej z otwartą domyślną stroną aplikacji (rysunek 4.5).
Rysunek 4.5. Nowa aplikacja w oknie przeglądarki
137
Java EE 6. Tworzenie aplikacji w NetBeans 7
Modyfikowanie strony w celu pobierania danych od użytkowników Wygenerowana aplikacja jest oczywiście tylko punktem wyjścia do tworzenia nowego programu. Teraz zobaczysz, jak zmodyfikować wygenerowany plik index.xhtml, aby pobierać dane od użytkowników. Najpierw trzeba dodać do strony znacznik . Jest to odpowiednik znacznika ze standardowych stron HTML. Wprowadzając na stronie kilka pierwszych znaków znacznika i wciskając kombinację Ctrl+Spacja, można wykorzystać znakomity mechanizm uzupełniania kodu środowiska NetBeans (rysunek 4.6).
Rysunek 4.6. Działanie mechanizmu uzupełniania kodu
Po dodaniu znacznika i kilku innych znaczników JSF strona powinna wyglądać tak:
Rejestracja
Strona rejestracji
138
Rozdział 4. • Tworzenie aplikacji sieciowych
Rysunek 4.7 przedstawia, jak strona będzie wyglądać w czasie wykonywania aplikacji.
Rysunek 4.7. Nowa strona w przeglądarce
Wszystkie pola JSF obsługujące dane wejściowe trzeba umieścić w znaczniku . Znacznik pomaga łatwo rozmieścić znaczniki JSF na stronie. Znacznik ten można traktować jak siatkę, w której umieszczane są znaczniki JSF. Atrybut columns znacznika określa liczbę kolumn siatki. Każdy komponent JSF w komponencie
139
Java EE 6. Tworzenie aplikacji w NetBeans 7
jest umieszczany w jednej z komórek siatki. Gdy w komponencie znajdzie się liczba komponentów równa wartości atrybutu columns (w przykładzie jest to 3), automatycznie rozpoczęty zostanie nowy wiersz. W poniższej tabelce pokazano, jak znaczniki są porządkowane w komponencie : Pierwszy znacznik
Drugi znacznik
Trzeci znacznik
Czwarty znacznik
Piąty znacznik
Szósty znacznik
Siódmy znacznik
Ósmy znacznik
Dziewiąty znacznik
Każdy wiersz w komponencie zawiera znacznik , pole na dane wejściowe i znacznik . Atrybut columnClasses komponentu pozwala przypisać styl CSS każdej kolumnie. Wartością tego atrybutu jest rozdzielona przecinkami lista stylów CSS zdefiniowanych w arkuszu stylów. Pierwszy styl dotyczy pierwszej kolumny, drugi — drugiej, a trzeci — trzeciej. Gdyby komponent miał więcej niż trzy kolumny, dla czwartej użyty zostałby pierwszy styl z atrybutu columnClasses, dla piątej — drugi itd. Jeśli chcesz określić styl wierszy komponentu , możesz zastosować atrybut rowClasses. Działa on tak samo jak atrybut columnClasses dla kolumn. Zwróć uwagę na znacznik w znaczniku w początkowej części strony. Jest to nowy znacznik wprowadzony w platformie JSF 2.0. Jedną z nowych funkcji platformy JSF 2.0 są katalogi na standardowe zasoby. Zasoby (np. arkusze stylów CSS, pliki JavaScript, obrazki itd.) można umieścić w katalogu najwyższego poziomu resources, a znaczniki JSF automatycznie uzyskują dostęp do tych zasobów. W tworzonym projekcie środowiska NetBeans katalog resources należy umieścić w katalogu Web Pages (rysunek 4.8).
Rysunek 4.8. Katalog resources z automatycznie dostępnymi zasobami
Następnie należy utworzyć podkatalog na arkusz stylów CSS (zgodnie z konwencją katalog ten powinien nosić nazwę css) i umieścić arkusz w tym podkatalogu. Arkusz stylów w omawianym przykładzie jest bardzo prosty, dlatego pominięto tu jego kod. Arkusz ten znajdziesz w kodzie do tego rozdziału, który możesz pobrać z witryny wydawnictwa Helion.
140
Rozdział 4. • Tworzenie aplikacji sieciowych
Wartością atrybutu library w znaczniku musi być nazwa katalogu z plikiem CSS, a w atrybucie name trzeba określić nazwę tego pliku. W katalogu resources oprócz plików CSS należy umieścić pliki JavaScript. Trzeba je zapisać w podkatalogu javascript. Następnie można uzyskać dostęp do tych plików w znaczniku , podając "javascript" jako wartość atrybutu library i nazwę pliku jako wartość atrybutu name. Podobnie obrazki należy umieścić w podkatalogu images katalogu resources. Dostęp do obrazków można uzyskać w znaczniku JSF . Wartością atrybutu library powinno być "images", a wartością atrybutu name — nazwa odpowiedniego pliku. Po omówieniu sposobu rozmieszczania elementów na stronie i dostępu do zasobów pora skoncentrować się na elementach na dane wejściowe i wyjściowe. Znacznik generuje w formularzu etykietę dla pola na dane wejściowe. Wartością atrybutu for powinna być wartość atrybutu id powiązanego pola. Znacznik generuje komunikat o błędzie dla pola na dane wejściowe. Wartością atrybutu for powinna być wartość atrybutu id powiązanego pola. Pierwszy wiersz siatki zawiera znacznik . Znacznik ten powoduje wygenerowanie na wyświetlanej stronie HTML-owego znacznika . Każdy znacznik JSF ma atrybut id. Wartością tego atrybutu musi być łańcuch znaków z unikatowym identyfikatorem. Jeśli nie określisz wartości tego atrybutu, zostanie ona wygenerowana automatycznie. Warto jawnie określić identyfikator każdego komponentu, ponieważ identyfikatory są podawane w komunikatach o błędach czasu wykonania. Znacznie łatwiej jest zidentyfikować problematyczny komponent, jeśli ma jawnie ustawiony identyfikator. Przy stosowaniu znaczników do generowania etykiet pól na dane wejściowe lub używaniu znaczników do generowania błędów związanych ze sprawdzaniem poprawności trzeba bezpośrednio podać wartość atrybutu id pola, ponieważ jest ona niezbędna w atrybucie for powiązanego znacznika lub . W platformie JSF każdy znacznik na dane wejściowe ma atrybut label. Atrybut ten służy do umieszczania na generowanych stronach komunikatów o błędach związanych ze sprawdzaniem poprawności. Jeśli nie określisz wartości atrybutu label, pole będzie można zidentyfikować w komunikacie o błędzie na podstawie identyfikatora. Ponadto każde pole na dane wejściowe ma atrybut value. W znaczniku atrybut ten określa, które opcje wygenerowanego znacznika mają być zaznaczone. Wartość tego atrybutu musi odpowiadać wartości atrybutu itemValue jednego z zagnieżdżonych znaczników . Wartością tego ostatniego atrybutu jest zwykle wyrażenie wiążące wartość. Oznacza to, że wartość jest wczytywana w czasie wykonywania programu z zarządzanego ziarna JSF. W przykładowym kodzie używane jest wiążące wartość wyrażenie 141
Java EE 6. Tworzenie aplikacji w NetBeans 7
#{registrationBean.salutation}. W czasie wykonywania programu JSF szuka zarządzanego ziarna o nazwie registrationBean i sprawdza atrybut salutation w tym ziarnie. Następnie
wywołuje getter dla tego atrybutu i używa zwróconej wartości do ustalenia, jaki element ma być zaznaczony w wygenerowanym HTML-owym znaczniku . W znaczniku znajduje się grupa znaczników . Powodują one wygenerowanie HTML-owych znaczników w znaczniku odpowiadającym znacznikowi . Wartością atrybutu itemLabel jest wartość wyświetlana użytkownikom, natomiast wartość atrybutu itemValue jest przekazywana do serwera w momencie przesłania formularza. We wszystkich pozostałych wierszach siatki znajdują się znaczniki . Powodują one wygenerowanie HTML-owych pól input typu text. Można w nich zapisać jeden wiersz tekstu. Atrybut id wszystkich pól jest jawnie ustawiany, co pozwala wskazywać te pola w powiązanych polach i . Ponadto we wszystkich znacznikach ustawiony jest atrybut label, dzięki czemu komunikaty o błędach są bardziej przyjazne dla użytkowników. W niektórych polach użytkownik musi wprowadzić wartość. W tych polach atrybut required ma wartość true. W platformie JSF wszystkie pola na dane wejściowe mają ten atrybut. Jeśli użytkownik musi wprowadzić wartość w danym polu, atrybut ten należy ustawić na wartość true. Atrybut ten jest opcjonalny. Jeśli nie określisz jego wartości, domyślnie będzie miał wartość false. W ostatnim wierszu siatki znajduje się pusty znacznik . Służy on do dodawania różnych znaczników do jednej komórki komponentu . Znaczniki umieszczone w są umieszczane w tej samej komórce siatki. Tu znacznik ma tylko tworzyć pustą komórkę, dzięki czemu następny znacznik () jest na wygenerowanej stronie wyrównany względem pól na dane wejściowe. Znacznik służy do przesyłania formularza na serwer. Wartość atrybutu value tego znacznika pozwala określić tekst generowanego przycisku. Wartość atrybutu action służy do określenia strony wyświetlanej w reakcji na wciśnięcie przycisku. W przykładowej aplikacji używana jest nawigacja statyczna. W platformie JSF przy stosowaniu takiej nawigacji wartość atrybutu action jest zapisywana na stałe w kodzie. Gdy używasz nawigacji statycznej, wartość atrybutu action w znaczniku odpowiada nazwie docelowej strony, jednak bez rozszerzenia .xhtml. Tu, gdy użytkownik kliknie przycisk, aplikacja ma przejść do pliku confirmation.xhtml. Dlatego wartością atrybutu action jest "confirmation". Zamiast nawigacji statycznej można zastosować nawigację dynamiczną. Wtedy wartością atrybutu action przycisku jest wyrażenie, które odpowiada metodzie zwracającej obiekt typu String z zarządzanego ziarna. Metoda ta może, w zależności od warunków, zwracać różne
142
Rozdział 4. • Tworzenie aplikacji sieciowych
wartości, a system nawigacji przechodzi do odpowiednich stron w zależności od tego, jaka wartość została zwrócona. Przy stosowaniu nawigacji dynamicznej metoda zarządzanego ziarna może zawierać dowolny kod, przy czym musi zwracać wartość typu String. Metoda ta często służy do zapisywania danych z zarządzanego ziarna w bazie. Jeśli używasz nawigacji dynamicznej, wartość zwracana w momencie kliknięcia przycisku przez metodę musi odpowiadać nazwie docelowej strony (bez rozszerzenia). We wcześniejszych wersjach platformy JSF trzeba było podać w pliku faces-config.xml reguły nawigacji. Dzięki pojawieniu się konwencji opisanych w poprzednich akapitach reguły te nie są już niezbędne.
Tworzenie własnego ziarna zarządzanego Zarządzane ziarna JSF to standardowe ziarna JavaBeans służące do przechowywania danych od użytkowników w aplikacjach JSF. Aby utworzyć nowe ziarno zarządzane, należy wybrać opcję File/New File…, otworzyć kategorię JavaServer Faces, a następnie kliknąć pozycję JSF Managed Bean na liście typów plików (rysunek 4.9).
Rysunek 4.9. Tworzenie nowego ziarna zarządzanego
143
Java EE 6. Tworzenie aplikacji w NetBeans 7
Na następnym ekranie kreatora należy wprowadzić nazwę zarządzanego ziarna i pakietu (rysunek 4.10).
Rysunek 4.10. Konfigurowanie nowego ziarna
Większość wartości domyślnych jest odpowiednia i nie trzeba ich zmieniać. Jedyne ustawienie, które w razie potrzeby należy zmodyfikować, to określające zasięg pole Scope. Ziarna zarządzane mogą działać w różnych zasięgach. Zasięg ograniczony do żądania sprawia, że ziarno jest dostępne tylko w jednym żądaniu HTTP. Ziarna zarządzane mogą też działać w zasięgu sesji. Wtedy są dostępne w sesji HTTP jednego użytkownika. Zasięg aplikacji sprawia, że ziarno jest dostępne dla wszystkich użytkowników aplikacji w wielu sesjach. Można też nie konfigurować żadnego zasięgu. Wtedy ziarno zarządzane nie jest przechowywane na żadnym poziomie, tylko tworzone na żądanie. Ponadto ziarna zarządzane mogą działać w zasięgu widoku. Wtedy ziarno jest dostępne do czasu przejścia użytkownika do następnej strony. Ziarna zarządzane o zasięgu widoku są dostępne w kolejnych żądaniach ajaksowych. Należy określić zasięg odpowiedni dla danego ziarna zarządzanego. W omawianym przykładzie właściwy jest domyślny zasięg żądania. Po zakończeniu działania kreatora w określonym pakiecie tworzona jest szablonowa wersja zarządzanego ziarna.
144
Rozdział 4. • Tworzenie aplikacji sieciowych
Kod źródłowy wygenerowanego zarządzanego ziarna obejmuje klasę ziarna z adnotacją i jednym publicznym konstruktorem bezargumentowym. package com.ensode.jsf.managedbeans; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; @ManagedBean @RequestScoped public class RegistrationBean { /** Tworzy nowy obiekt typu RegistrationBean */ public RegistrationBean() { } }
Adnotacja @ManagedBean powoduje oznaczenie klasy jako zarządzanego ziarna JSF. Nazwa zarządzanego ziarna domyślnie odpowiada nazwie klasy (tu nazwa klasy to RegistrationBean) z pierwszą literą przekształconą na małą. Tak więc w przykładzie nazwą ziarna jest registrationBean. Jeśli chcesz zmienić domyślną nazwę, możesz określić inną w kreatorze New JSF Managed Bean środowiska NetBeans. Możesz też ustawić atrybut name w adnotacji @ManagedBean na inną wartość. Zwykle jednak zachowanie wartości domyślnych poprawia czytelność kodu i ułatwia jego konserwację. Dlatego jeśli nie ma ku temu dobrych powodów, nie należy zmieniać domyślnej nazwy. Po dodaniu do projektu dowolnej klasy Javy z adnotacją @ManagedBean nie trzeba rejestrować serwletu FacesServlet w pliku web.xml, ponieważ środowisko uruchomieniowe JSF serwera aplikacji automatycznie rejestruje ten serwlet. Adnotacja @RequestScoped oznacza, że dane zarządzane ziarno ma zasięg żądania. Gdyby w czasie tworzenia zarządzanego ziarna w kreatorze środowiska NetBeans ustawić inny zasięg, użyta zostałaby inna adnotacja, odpowiednia dla wybranego zasięgu. Dla zarządzanych ziaren o zasięgu sesji używana jest adnotacja @SessionScoped, a dla zarządzanych ziaren bez określonego zasięgu stosuje się adnotację @NoneScoped. Zarządzane ziarna o zasięgu widoku mają adnotację @ViewScoped. Na tym etapie należy zmodyfikować zarządzane ziarno i dodać właściwości przeznaczone na wartości wprowadzone przez użytkowników. Automatyczne generowanie getterów i setterów Środowisko NetBeans może automatycznie generować gettery i settery dla właściwości. Wystarczy kliknąć skrót klawiaturowy dla operacji wstawiania kodu (Alt+Insert w systemach Windows i Linux), a następnie wybrać opcję Getters and Setters. package com.ensode.jsf.managedbeans; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; @ManagedBean @RequestScoped public class RegistrationBean {
145
Java EE 6. Tworzenie aplikacji w NetBeans 7
/** Tworzy nowy obiekt typu RegistrationBean */ public RegistrationBean() { } private String salutation; private String firstName; private String lastName; private Integer age; private String email; // W celu zachowania zwięzłości gettery i settery zostały pominięte }
Zauważ, że nazwy wszystkich właściwości ziarna (zmienne egzemplarza) pasują do nazw używanych w wyrażeniach wiążących wartości ze strony. Nazwy te muszą do siebie pasować, aby platforma JSF potrafiła powiązać właściwości ziarna z wyrażeniami wiążącymi wartości.
Tworzenie strony z potwierdzeniem Gdy użytkownik wypełni dane na przeznaczonej do tego stronie i prześle formularz, aplikacja ma wyświetlać stronę z potwierdzeniem obejmującą wartości wprowadzone przez użytkownika. Ponieważ na stronie wejściowej dla każdego pola na dane zastosowano wyrażenia wiążące wartości, odpowiednie pola w ziarnie zarządzanym są zapełniane danymi od użytkownika. Dlatego na stronie z potwierdzeniem wystarczy wyświetlić dane z zarządzanego ziarna, używając serii znaczników JSF . Stronę z potwierdzeniem można utworzyć za pomocą kreatora New JSF File (rysunek 4.11).
Rysunek 4.11. Tworzenie strony z potwierdzeniem
146
Rozdział 4. • Tworzenie aplikacji sieciowych
Trzeba się upewnić, że nazwa nowego pliku (confirmation.xhtml) pasuje do wartości atrybutu action przycisku ze strony do wprowadzania danych. Dzięki temu nawigacja statyczna będzie działać poprawnie. Wygenerowana strona po dopasowaniu do wymagań powinna wyglądać tak:
Strona z potwierdzeniem
Strona z potwierdzeniem
Jak widać, strona z potwierdzeniem jest bardzo prosta. Zawiera grupę znaczników z etykietami i wyrażeniami wiążącymi wartości powiązanymi z właściwościami ziarna zarządzanego. Znacznik JSF wyświetla na generowanej stronie wartość wyrażenia z atrybutu value.
Uruchamianie aplikacji Teraz można uruchomić nową aplikację JSF. Najłatwiej w tym celu kliknąć projekt prawym przyciskiem myszy i wybrać opcję Run z menu podręcznego. Wtedy GlassFish (lub inny serwer aplikacji używany w projekcie) automatycznie się uruchomi (jeśli nie był już włączony), a domyślna przeglądarka zostanie skierowana na adres URL strony. Po wprowadzeniu danych strona powinna wyglądać tak jak na rysunku 4.12.
147
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 4.12. Nowa aplikacja w przeglądarce
Gdy klikniesz przycisk Zarejestruj, ziarno zarządzane RegistrationBean zostanie zapełnione wprowadzonymi na stronie wartościami. Właściwości powiązane z polami zostaną zapełnione na podstawie wyrażeń wiążących wartości z poszczególnych pól na dane wejściowe. Na tym etapie uruchamiana jest nawigacja JSF i użytkownik przechodzi do strony z potwierdzeniem (rysunek 4.13).
Rysunek 4.13. Strona z potwierdzeniem
Wartości widoczne na stronie z potwierdzeniem pochodzą z ziarna zarządzanego. Jest to dowód na to, że właściwości ziarna zostały prawidłowo zapełnione.
Sprawdzanie poprawności danych w JSF Wcześniej w rozdziale podano, że atrybut required pól na dane wejściowe w JSF umożliwia łatwe tworzenie wymaganych pól tego rodzaju.
148
Rozdział 4. • Tworzenie aplikacji sieciowych
Jeśli użytkownik spróbuje przesłać formularz, w którym przynajmniej jedno wymagane pole jest puste, automatycznie wygenerowany zostanie komunikat o błędzie (rysunek 4.14).
Rysunek 4.14. Automatycznie generowany komunikat o błędzie
Komunikat o błędzie jest generowany przez znacznik odpowiadający błędnemu polu. Łańcuch znaków Imię w komunikacie o błędzie to wartość atrybutu label tego pola. Gdyby ten atrybut nie istniał, komunikat zawierałby wartość atrybutu id pola. Jak widać, atrybut required sprawia, że można bardzo łatwo zaimplementować wymagane pola w aplikacji. Pole age jest powiązane z właściwością typu Integer z ziarna zarządzanego. Jeśli użytkownik wpisze w tym polu wartość inną niż liczba całkowita, automatycznie wygenerowany zostanie błąd związany ze sprawdzaniem poprawności (rysunek 4.15).
Rysunek 4.15. Błąd automatycznie generowany w wyniku sprawdzania poprawności danych
149
Java EE 6. Tworzenie aplikacji w NetBeans 7
Oczywiście wpisywanie ujemnego wieku nie ma sensu. Jednak aplikacja sprawdza, czy dane wprowadzone przez użytkownika są liczbą całkowitą, i nie wymaga to niemal żadnego wysiłku od programisty. Pole na adres e-mail jest powiązane z właściwością typu String z ziarna zarządzanego. Dla tego pola nie ma wbudowanego walidatora, który sprawdzałby, czy użytkownik wprowadził prawidłowy adres e-mail. W takiej sytuacji trzeba samodzielnie utworzyć niestandardowy walidator w JSF. W niestandardowym walidatorze w JSF trzeba zaimplementować interfejs javax.faces.validator. Validator. Interfejs ten zawiera jedną metodę, validate(). Metoda ta przyjmuje trzy parametry: obiekt typu javax.faces.context.FacesContext, obiekt typu javax.faces.component. UIComponent zawierający sprawdzany komponent JSF i obiekt typu java.lang.Object z wprowadzoną przez użytkownika wartością komponentu. Typowy niestandardowy walidator przedstawiony jest w poniższym kodzie: package com.ensode.jsf.validators; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.component.html.HtmlInputText; import javax.faces.context.FacesContext; import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException; @FacesValidator("emailValidator") public class EmailValidator implements Validator { public void validate(FacesContext facesContext, UIComponent uIComponent, Object value) t ValidatorException { Pattern pattern = Pattern.compile("\\w+@\\w+\\.\\w+"); Matcher matcher = pattern.matcher( (CharSequence) value); HtmlInputText htmlInputText = (HtmlInputText) uIComponent; String label; if (htmlInputText.getLabel() == null || htmlInputText.getLabel().trim().equals("")) { label = htmlInputText.getId(); } else { label = htmlInputText.getLabel(); } if (!matcher.matches()) { FacesMessage facesMessage = new FacesMessage(label + ": to nie jest prawidłowy adres e-mail");
150
Rozdział 4. • Tworzenie aplikacji sieciowych
throw new ValidatorException(facesMessage); } } }
W przykładowym kodzie metoda validate() dopasowuje wyrażenie regularne do wartości sprawdzanego komponentu JSF. Jeśli wartość pasuje do wyrażenia, sprawdzanie poprawności kończy się sukcesem. W przeciwnym razie sprawdzanie poprawności kończy się niepowodzeniem i zgłaszany jest wyjątek typu javax.faces.validator.ValidatorException. Przykładowy niestandardowy walidator ma przede wszystkim pokazać, jak łatwo dodać niestandardowe sprawdzanie poprawności danych w JSF. Nie ma to być niezawodny walidator adresów e-mail. Może on wskazać poprawny adres jako błędny.
Konstruktor klasy ValidatorException przyjmuje jako parametr obiekt typu javax.faces. application.FacesMessage. Obiekt ten służy do wyświetlania na stronie komunikatu o błędzie, gdy sprawdzanie poprawności zakończy się niepowodzeniem. Komunikat jest przekazywany jako obiekt typu String do konstruktora klasy FacesMessage. Jeśli atrybut label w przykładowym kodzie jest różny od null i nie jest pusty, zostaje użyty w komunikacie o błędzie. W przeciwnym razie należy użyć wartości atrybutu id komponentu. To rozwiązanie jest zgodne ze wzorcem stosowanym w standardowych walidatorach w JSF. Do walidatora trzeba dodać adnotację @FacesValidator. Wartością atrybutu value jest identyfikator, który posłuży do wskazywania walidatora na stronach JSF. Po napisaniu walidatora można wykorzystać go na stronach. W omawianym przykładzie należy zmodyfikować pole email i zastosować w nim niestandardowy walidator:
Wystarczy zagnieździć znacznik w polu na dane wejściowe, które ma być sprawdzane za pomocą niestandardowego walidatora. Wartość atrybutu validatorId tego znacznika musi pasować do wartości atrybutu value z adnotacji @FacesValidator walidatora. Na tym etapie można przetestować niestandardowy walidator (rysunek 4.16). Gdy w odpowiednim polu wpiszesz nieprawidłowy adres e-mail i prześlesz formularz, aplikacja uruchomi kod niestandardowego walidatora i wyświetli obiekt typu String przekazany jako parametr do klasy FacesMessage w metodzie validator(). Przekazany łańcuch znaków jest wyświetlany jako tekst komunikatu o błędzie w znaczniku pola.
151
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 4.16. Działanie niestandardowego walidatora
Tworzenie szablonów faceletów Jedną z zalet faceletów w porównaniu ze stronami JSP jest mechanizm szablonów. Szablony pozwalają utworzyć układ strony w jednym miejscu. Następnie można wykorzystać zdefiniowany w szablonie układ w klientach szablonu. Ponieważ większość aplikacji sieciowych ma spójny układ na wszystkich stronach, zastosowanie szablonów ułatwia konserwację aplikacji, ponieważ układ można modyfikować w jednym miejscu. Jeśli w pewnym momencie trzeba zmienić układ stron (np. dodać stopkę lub przenieść kolumnę z lewej części strony na prawą), wystarczy zmodyfikować szablon. Zmiany zostaną wtedy odzwierciedlone we wszystkich klientach szablonu. Środowisko NetBeans zapewnia bardzo dobrą obsługę szablonów faceletów. Dostępnych jest kilka gotowych szablonów opartych na często używanych układach stron internetowych. Tworzenie szablonu przedstawia rysunek 4.17. Następnie można wybrać jeden z kilku wbudowanych szablonów i użyć go jako punktu wyjścia do rozwijania własnego szablonu lub zastosować w pierwotnej postaci (rysunek 4.18). Środowisko NetBeans umożliwia zastosowanie układu opartego na tabelach HTML lub stylach CSS. W większości tworzonych obecnie aplikacji zalecane jest używanie stylów CSS. W przedstawianym przykładzie używany jest układ z nagłówkiem, kolumną po lewej stronie i obszarem głównym. Gdy klikniesz przycisk Finish, środowisko NetBeans automatycznie wygeneruje szablon wraz z potrzebnymi plikami CSS.
152
Rozdział 4. • Tworzenie aplikacji sieciowych
Rysunek 4.17. Tworzenie nowego szablonu faceletu
Rysunek 4.18. Możesz wybrać jeden z wbudowanych szablonów
153
Java EE 6. Tworzenie aplikacji w NetBeans 7
Automatycznie wygenerowany szablon wygląda tak:
Facelets Template
Top
Left
Content
Jak widać, szablonowy plik nie różni się bardzo od zwykłych plików faceletów.
Dodawanie szablonu faceletu do projektu Aby dodać szablon faceletu do projektu, wystarczy wybrać opcję File/New File, otworzyć kategorię JavaServer Faces, a następnie wybrać typ pliku Facelets Template. Zauważ, że w szablonie używana jest następująca przestrzeń nazw: xmlns:ui="http://java. sun.com/jsf/facelets". Umożliwia ona użycie znacznika , którego zawartość jest zastępowana elementami z powiązanego znacznika z klientów szablonu.
Używanie szablonów Aby zastosować szablon, należy utworzyć klienta szablonu faceletu. Aby to zrobić, wybierz opcję File/New File, otwórz kategorię JavaServer Faces, a następnie kliknij typ pliku Facelets Template Client (rysunek 4.19).
154
Rozdział 4. • Tworzenie aplikacji sieciowych
Rysunek 4.19. Tworzenie klienta szablonu faceletu
Po kliknięciu przycisku Next> wpisz nazwę pliku (lub zaakceptuj nazwę domyślną) i wybierz szablon, który chcesz zastosować w kliencie (rysunek 4.20).
Rysunek 4.20. Wybieranie szablonu dla klienta
155
Java EE 6. Tworzenie aplikacji w NetBeans 7
Gdy klikniesz przycisk Finish, klient szablonu zostanie wygenerowany:
top
left
content
Jak widać, w kliencie szablonu też używana jest przestrzeń nazw xmlns:ui="http://java.sun. com/jsf/facelets". W kliencie każdy znacznik z tej przestrzeni nazw musi znajdować się w znaczniku . Kod spoza znacznika nie jest wyświetlany; zamiast tego generowany jest kod szablonu. Znacznik służy do wstawiania kodu do odpowiedniego znacznika z szablonu. Wartość atrybutu name w znaczniku musi pasować do odpowiedniego znacznika z szablonu. Po zainstalowaniu aplikacji można zobaczyć, jak działają szablony. W tym celu należy wpisać w przeglądarce adres URL klienta szablonu (rysunek 4.21).
Rysunek 4.21. Działanie szablonu
Warto zauważyć, że środowisko NetBeans wygenerowało szablon, który pozwala stosunkowo małym nakładem pracy utworzyć elegancką stronę. Oczywiście należy zastąpić kod ze znaczników potrzebnym kodem.
156
Rozdział 4. • Tworzenie aplikacji sieciowych
Poniżej została przedstawiona zmodyfikowana wersja szablonu. Dodano w niej kod wyświetlany w odpowiednich miejscach szablonu.
Witaj w witrynie
Odnośniki
-
-
-
-
-
-
157
Java EE 6. Tworzenie aplikacji w NetBeans 7
-
W tym głównym obszarze należy umieścić podstawowy tekst, grafikę, formularze itd. Tu znajduje się typowy zapełniacz, tak lubiany przez programistów aplikacji sieciowych.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc venenatis, diam nec tempor dapibus, lacus erat vehicula mauris, id lacinia nisi arcu vitae purus. Nam vestibulum nisi non lacus luctus vel ornare nibh pharetra. Aenean non lorem lectus, eu tempus lectus. Cras mattis nibh a mi pharetra ultricies. In consectetur, tellus sit amet pretium facilisis, enim ipsum consectetur magna, a mattis ligula massa vel mi. Maecenas id arcu a erat pellentesque vestibulum at vitae nulla. Nullam eleifend sodales tincidunt. Donec viverra libero non erat porta sit amet convallis enim commodo. Cras eu libero elit, ac aliquam ligula. Quisque a elit nec ligula dapibus porta sit amet a nulla. Nulla vitae molestie ligula. Aliquam interdum, velit at tincidunt ultrices, sapien mauris sodales mi, vel rutrum turpis neque id ligula. Donec dictum condimentum arcu ut convallis. Maecenas blandit, ante eget tempor sollicitudin, ligula eros venenatis justo, sed ullamcorper dui leo id nunc. Suspendisse potenti. Ut vel mauris sem. Duis lacinia eros laoreet diam cursus nec hendrerit tellus pellentesque.
Po wprowadzeniu tych zmian klient szablonu powinien wyglądać tak jak na rysunku 4.22. Jak widać, tworzenie szablonów faceletów i klientów szablonów w środowisku NetBeans jest bardzo łatwe.
158
Rozdział 4. • Tworzenie aplikacji sieciowych
Rysunek 4.22. Wygląd klienta szablonu z przykładowymi danymi
Komponenty złożone Bardzo wygodną cechą platformy JSF 2.0 jest możliwość łatwego pisania niestandardowych komponentów JSF. W JSF 2 wymaga to niewiele więcej niż utworzenia kodu komponentu. Nie jest potrzebny ani kod w Javie, ani konfigurowanie komponentu. Ponieważ niestandardowe komponenty zwykle składają się z innych komponentów JSF, nazywa się je komponentami złożonymi. Aby wygenerować komponent złożony, wybierz opcję File/New, otwórz kategorię JavaServer Faces, a następnie kliknij typ pliku JSF Composite Component (rysunek 4.23). Po kliknięciu przycisku Next> można określić nazwę pliku, projekt i folder niestandardowego komponentu (4.24). Aby wykorzystać automatyczną obsługę zasobów i konwencje platformy JSF 2.0, nie należy zmieniać katalogu przeznaczonego na niestandardowy komponent. Gdy klikniesz przycisk Finish, środowisko NetBeans wygeneruje pusty komponent złożony. Można go wykorzystać jako punkt wyjścia do rozwijania własnego komponentu.
Każdy komponent złożony w platformie JSF 2.0 obejmuje dwie sekcje: interfejsu i implementacji. Sekcję interfejsu trzeba umieścić w znaczniku . W interfejsie należy zdefiniować atrybuty komponentu. W sekcji implementacji umieszcza się kod generowany, gdy programista użyje danego komponentu złożonego. W omawianym przykładzie powstanie prosty komponent służący do wprowadzania adresów. Dzięki temu, jeśli w aplikacji trzeba będzie wpisać kilka adresów, będzie można umieścić logikę i (lub) warstwę prezentacji w komponencie. Jeżeli później trzeba będzie zmodyfikować pola przeznaczone na adres (na przykład w celu dodania obsługi adresów z różnych państw), wystarczy wprowadzić zmiany w komponencie, a wszystkie formularze z adresami w aplikacji zostaną automatycznie zaktualizowane. Po uzupełnieniu pustych miejsc komponent złożony powinien wyglądać tak:
161
Java EE 6. Tworzenie aplikacji w NetBeans 7
Atrybuty komponentu ustawiane są za pomocą znacznika . Znacznik ten ma atrybut name, w którym można podać nazwę atrybutu, oraz opcjonalny atrybut required, pozwalający określić, czy atrybut jest wymagany. Ciało znacznika bardzo przypomina zwykły kod JSF. Różnica jest jedna — zgodnie z konwencją dostęp do atrybutów znacznika można uzyskać za pomocą wyrażenia #{cc.attrs.NAZWA_ATRYBUTU}. W ten sposób można użyć atrybutów zdefiniowanych w sekcji interfejsu komponentu. Zauważ, że atrybut managedBean komponentu musi odpowiadać ziarnu zarządzanemu JSF. Na stronach, na których używany jest komponent, trzeba jako wartość tego atrybutu podać wyrażenie JSF zwracające ziarno zarządzane. Dostęp do atrybutów tego ziarna zarządzanego można uzyskać za pomocą standardowej, używanej już wcześniej notacji .property. Jedyna różnica polega na tym, że zamiast używać w wyrażeniu nazwy ziarna zarządzanego, trzeba podać nazwę atrybutu zdefiniowaną w sekcji interfejsu. Gdy prosty, ale kompletny komponent złożony jest już gotowy, można bardzo łatwo zastosować go na stronach.
Wprowadzanie adresu
Zgodnie z konwencją przestrzenią nazw niestandardowych komponentów zawsze jest xmlns:ezcomp="http://java.sun.com/jsf/composite/ezcomp". To dlatego nie należy zmieniać domyślnego katalogu na komponent (ustawienie innego katalogu powoduje niezgodność z tą konwencją). Środowisko NetBeans zapewnia mechanizm uzupełniania kodu dla niestandardowych komponentów złożonych. Działa on tak samo jak dla standardowych komponentów. W przykładowej aplikacji znajduje się ziarno zarządzane addressBean. Jest to proste ziarno o kilku właściwościach oraz powiązanych z nimi getterach i setterach, dlatego pominięto tu jego kod (znajdziesz go w dostępnym do pobrania kodzie do tego rozdziału). Ziarno to jest używane jako wartość atrybutu managedBean komponentu. Ponadto podano "domowy" jako wartość atrybutu addrType. Wartość ta jest wyświetlana jako nagłówek komponentu do wprowadzania adresów. Po zainstalowaniu i uruchomieniu aplikacji zobaczysz, jak działa utworzony komponent (rysunek 4.25).
Rysunek 4.25. Komponent wyświetlony na stronie
Jak widać, tworzenie komponentów złożonych JSF 2.0 w środowisku NetBeans jest bardzo łatwe.
163
Java EE 6. Tworzenie aplikacji w NetBeans 7
Podsumowanie W tym rozdziale zobaczyłeś, w jaki sposób środowisko NetBeans pomaga tworzyć nowe projekty JSF, automatycznie dodając wszystkie potrzebne biblioteki. Dowiedziałeś się, jak można szybko tworzyć strony JSF dzięki dostępnemu w środowisku NetBeans mechanizmowi uzupełniania kodu. Dowiedziałeś się też, w jaki sposób można znacznie przyspieszyć i ułatwić sobie pracę dzięki pozwoleniu środowisku NetBeans na wygenerowanie szablonów JSF 2.0. NetBeans generuje też niezbędne arkusze CSS, co umożliwia łatwe tworzenie eleganckich stron. Zobaczyłeś również, jak środowisko NetBeans pomaga w tworzeniu niestandardowych komponentów JSF 2.0.
164
5 Tworzenie eleganckich aplikacji sieciowych z wykorzystaniem biblioteki PrimeFaces Jedną z zalet platformy JSF jest to, że umożliwia bardzo łatwe tworzenie niestandardowych komponentów. Dlatego powstało kilka bibliotek komponentów o otwartym dostępie do kodu źródłowego. Jedną z nich jest biblioteka PrimeFaces. Umożliwia ona łatwe tworzenie eleganckich aplikacji sieciowych. W wersji 7.0 środowiska NetBeans dostępna jest wbudowana biblioteka PrimeFaces.
Pierwszy projekt utworzony z wykorzystaniem biblioteki PrimeFaces Aby zastosować bibliotekę PrimeFaces w projekcie, trzeba w standardowy sposób utworzyć aplikację sieciową w Javie. Gdy wybierzesz platformę JavaServer Faces, kliknij zakładkę Components, a następnie wybierz pozycję PrimeFaces 2.2.1 jako pakiet komponentów (rysunek 5.1).
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 5.1. Tworzenie aplikacji z biblioteką PrimeFaces
Po utworzeniu projektu środowisko NetBeans doda potrzebne biblioteki. Na stronach JSF dostępny wtedy będzie mechanizm automatycznego uzupełniania projektu dla znaczników biblioteki PrimeFaces. Jeśli wybierzesz PrimeFaces jako pakiet komponentów dla projektu JSF, środowisko NetBeans utworzy przykładowy projekt używający komponentów PrimeFaces. Kod wygenerowanego pliku wygląda tak:
Facelet Title
For more information visit http://primefaces.org.
Strona ta wygląda prawie jak zwykła strona faceletu. Wyjątkiem jest kilka komponentów charakterystycznych dla biblioteki PrimeFaces. Zwróć uwagę na to, że przestrzeń nazw biblioteki PrimeFaces (xmlns:p="http://primefaces. prime.com.tr/ui") jest automatycznie dodawana do znacznika . Ta przestrzeń nazw jest niezbędna do stosowania komponentów PrimeFaces na stronach. Zgodnie z konwencją znaczniki PrimeFaces mają przedrostek p. Pierwszy komponent PrimeFaces na przedstawionej stronie to . Jest on podobny do standardowego komponentu przycisku polecenia JSF, jednak ma pewne dodatkowe zalety (na przykład wygląda elegancko bez konieczności ręcznego dodawania arkusza stylów CSS). Inny komponent PrimeFaces na przykładowej stronie to . Komponent ten jest wyświetlany jako okno dialogowe, które może znajdować się nad innymi komponentami strony. Za pomocą wartości atrybutu widgetVer można uzyskać dostęp do tego komponentu w innych komponentach ze strony. Okno dialogowe udostępnia w tym celu działający po stronie klienta interfejs API JavaScriptu. Najczęściej używane funkcje tego interfejsu API to show() i hide(). Służą one do wyświetlania i ukrywania okna dialogowego na stronie. Ten interfejs API wykorzystano w atrybucie onclick wspomnianego wcześniej przycisku polecenia. Gdy uruchomisz aplikację, zobaczysz automatycznie wygenerowaną stronę (rysunek 5.2).
Rysunek 5.2. Automatycznie wygenerowana strona
Po kliknięciu przycisku pojawi się okno dialogowe (rysunek 5.3).
167
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 5.3. Okno dialogowe wyświetlane w odpowiedzi na kliknięcie przycisku
Gdy klikniesz odnośnik w oknie dialogowym, przejdziesz do witryny biblioteki PrimeFaces (rysunek 5.4).
Rysunek 5.4. Witryna poświęcona bibliotece PrimeFaces
Jak widać, biblioteka PrimeFaces umożliwia tworzenie eleganckich aplikacji sieciowych małym nakładem sił. Dalej zobaczysz, jak wykorzystać niektóre komponenty PrimeFaces, aby znacznie ułatwić i uprościć proces tworzenia aplikacji sieciowych. 168
Rozdział 5. • Tworzenie eleganckich aplikacji sieciowych
Stosowanie komponentów PrimeFaces w aplikacjach JSF Z tego podrozdziału dowiesz się, jak stosować proste komponenty PrimeFaces, aby ułatwić sobie pracę nad tworzeniem aplikacji JSF. Poniżej znajduje się kod prostej strony do wprowadzania danych na temat klientów:
Wprowadzanie danych o klientach
169
Java EE 6. Tworzenie aplikacji w NetBeans 7
W przykładzie wykorzystano dostępny w środowisku NetBeans mechanizm generowania szablonów faceletu. Dzięki temu można „za darmo” uzyskać bardzo ciekawe style CSS. Więcej informacji o szablonach faceletów w środowisku NetBeans znajdziesz w poprzednim rozdziale.
Gdy uruchomisz projekt, przedstawiony kod zostanie wyświetlony w przeglądarce tak jak na rysunku 5.5.
Rysunek 5.5. Strona do wprowadzania danych w przeglądarce
Pierwszy nowy komponent PrimeFaces zastosowany na tej stronie to . Można go wykorzystać jako zamiennik standardowego komponentu JSF . Zaletą znacznika w porównaniu ze znacznikiem jest to, że komunikaty o błędach w znaczniku są domyślnie formatowane w elegancki sposób (rysunek 5.6).
170
Rozdział 5. • Tworzenie eleganckich aplikacji sieciowych
Rysunek 5.6. Sformatowane komunikaty o błędach w znaczniku
Podobnie jak w przypadku komponentu , biblioteka PrimeFaces udostępnia komponent , który można potraktować jako zamiennik standardowego komponentu JSF (w przykładzie nie zastosowano komponentu ). Znacznik , podobnie jak , należy stosować do wyświetlania błędów związanych ze sprawdzaniem poprawności obok pól z nieprawidłowymi danymi, a nie w górnej części strony. Inny komponent PrimeFaces wykorzystany w przykładzie to . Kalendarz ten można wykorzystać do wprowadzania dat. Gdy użytkownik kliknie ikonę wygenerowaną dla tego komponentu, pojawi się atrakcyjny widżet z kalendarzem (rysunek 5.7). Użytkownik może w nim wybrać datę, klikając ją. Komponent kalendarza biblioteki PrimeFaces można w szerokim zakresie modyfikować. Domyślnie jest wyświetlany jako pole tekstowe, a gdy użytkownik je kliknie, pojawia się widżet z kalendarzem. W przykładzie atrybut showOn ma wartość button. To powoduje, że obok pola tekstowego widoczna jest ikona kalendarza. Jest to wizualna wskazówka informująca o specjalnym charakterze pola. Pozwala to także wprowadzić datę ręcznie, jeśli użytkownik woli zastosować to podejście. Domyślnie listy miesięcy i lat są niedostępne, co utrudnia wprowadzanie dat z odległej przeszłości i przyszłości (domyślnym dniem jest zawsze dzień bieżący). Aby rozwiązać ten problem, można ustawić właściwość navigator na true, jak zrobiono w przykładowym kodzie. Ponadto można kontrolować wygląd pola tekstowego. Umożliwiają to atrybuty inputStyle i inputStyleClass komponentu . Wartością atrybutu inputStyle musi być poprawny wewnątrzwierszowy kod CSS, natomiast w atrybucie inputStyleClass trzeba podać nazwę klasy CSS zdefiniowanej w jednym z arkuszy stylów CSS. 171
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 5.7. Komponent kalendarza umożliwia wybranie daty
Ostatnim nowym komponentem PrimeFaces użytym na przykładowej stronie jest . Domyślnie wyświetla on przycisk, który zgłasza ajaksowe żądanie aktualizacji części strony. Nie jest wtedy konieczne odświeżanie całej strony. Komponent ten można wykorzystać jako zamiennik standardowego komponentu JSF . W tym celu właściwość ajax należy ustawić na false, tak jak w przykładowym kodzie. Zaletą stosowania komponentu zamiast jest to, że ma on domyślnie elegancki wygląd. Dzięki temu nie trzeba tworzyć niestandardowych stylów CSS dla przycisków. Gdy klikniesz przycisk na przykładowej stronie, przejdziesz do strony z potwierdzeniem (rysunek 5.8).
Rysunek 5.8. Strona z potwierdzeniem
172
Rozdział 5. • Tworzenie eleganckich aplikacji sieciowych
Na stronie z potwierdzeniem nie ma żadnych nowych komponentów PrimeFaces, dlatego strona ta nie wymaga omówienia. Znajdziesz ją w dostępnym do pobrania kodzie dla tego rozdziału (plik confirmation.xhtml).
Widoki z zakładkami W formularzach HTML często znajduje się dużo pól, przez co formularze mogą być bardzo długie. Programiści nieraz dzielą formularze na zakładki, dzięki czemu strona jest mniej przytłaczająca dla użytkowników. Zwykle tworzenie stron z zakładkami wymaga stosowania sztuczek z języków HTML i JavaScript. Jednak biblioteka PrimeFaces udostępnia komponent , który można wykorzystać do łatwego generowania zakładek. W poniższym przykładzie pokazano, jak korzystać z tego komponentu.
Wprowadzanie danych o klientach
173
Java EE 6. Tworzenie aplikacji w NetBeans 7
174
Rozdział 5. • Tworzenie eleganckich aplikacji sieciowych
179
Java EE 6. Tworzenie aplikacji w NetBeans 7
Niektóre strony („zakładki”) kreatora zostały pominięte w kodzie z uwagi na konieczność zachowania zwięzłości tego przykładu. Kompletną wersję znajdziesz w dostępnym do pobrania kodzie do tego rozdziału.
Jak widać, biblioteka PrimeFaces sprawia, że generowanie interfejsów opartych na kreatorze jest proste. Wystarczy zastosować komponent , a następnie dodać komponent dla każdego etapu kreatora. W każdym komponencie można umieścić standardowy kod JSF. W ostatniej zakładce znajduje się komponent służący do przesyłania danych na serwer. Wartość atrybutu actionListener tego komponentu to metoda z ziarna zarządzanego CustomerController.
180
Rozdział 5. • Tworzenie eleganckich aplikacji sieciowych
package com.ensode.primefacesdemo.managedbeans; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.event.ActionEvent; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; @ManagedBean @SessionScoped public class CustomerController implements Serializable { /** Tworzy nowy obiekt typu CustomerController */ public CustomerController() { } public void saveCustomer(ActionEvent actionEvent) { // W rzeczywistej aplikacji należałoby zapisać dane. // W tym przykładzie kod tylko wyświetla komunikat. FacesMessage facesMessage = new FacesMessage( "Dane zostały zapisane"); facesMessage.setSeverity(FacesMessage.SEVERITY_INFO); FacesContext.getCurrentInstance().addMessage(null, facesMessage); } }
Metoda saveCustomer() tylko ustawia komunikat wyświetlany jako potwierdzenie na stronie. W rzeczywistej aplikacji oczywiście należałoby zapisać dane w bazie. Na tym etapie można zobaczyć, jak działa komponent kreatora (rysunek 5.12).
Rysunek 5.12. Działanie komponentu kreatora
Zauważ, że komponent kreatora automatycznie dodaje przycisk Next w prawym dolnym rogu strony (rysunek 5.13). Kliknięcie tego przycisku powoduje przejście do następnej strony kreatora. 181
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 5.13. Przycisk Next pozwala przechodzić między stronami kreatora
Jak można się domyślić, komponent kreatora generuje zarówno przycisk Next, jak i przycisk Back. Po przejściu w kreatorze do ostatniej strony i kliknięciu przycisku Prześlij pojawi się komunikat z potwierdzeniem wygenerowany przez metodę saveCustomer() z ziarna zarządzanego CustomerController. Komunikat ten jest wyświetlany przez komponenty , które zapewniają elegancki styl tekstu bez konieczności jawnego stosowania stylów CSS (rysunek 5.14).
Rysunek 5.14. Komponent zapewnia elegancki styl komunikatu
182
Rozdział 5. • Tworzenie eleganckich aplikacji sieciowych
Dodatkowe informacje W tym rozdziale przedstawiono tylko podstawowe informacje na temat możliwości biblioteki PrimeFaces. Pokaz ilustrujący wszystkie komponenty z tej biblioteki znajdziesz na stronie http://www.primefaces.org/showcase/ui/home.jsf. Więcej informacji na temat tej biblioteki zawiera witryna http://www.primefaces.org.
Podsumowanie Ten rozdział zawiera wprowadzenie do biblioteki komponentów JSF PrimeFaces dostępnej w środowisku NetBeans od wersji 7.0. Pokazano tu, w jaki sposób biblioteka PrimeFaces umożliwia łatwe tworzenie eleganckich aplikacji z obsługą Ajaksa. Jednym z ważnych opisanych tu komponentów biblioteki PrimeFaces jest komponent zakładek, który umożliwia wygodne dzielenie stron na zakładki. Dowiedziałeś się też, jak stosować komponent kreatora z biblioteki PrimeFaces, pozwalający na łatwe tworzenie w aplikacjach sieciowych interfejsów opartych na kreatorach.
183
Java EE 6. Tworzenie aplikacji w NetBeans 7
184
6 Interakcja z bazami danych za pomocą interfejsu Java Persistence API Interfejs Java Persistence API (JPA) służy do tworzenia odwzorowań obiektowo-relacyjnych. Narzędzia tego typu pomagają w automatyzowaniu odwzorowań obiektów Javy na tabele z relacyjnych baz danych. We wcześniejszych wersjach J2EE do tworzenia takich odwzorowań służyły ziarna encyjne. Ziarna tego typu próbują zawsze zachowywać synchronizację danych w pamięci z bazą danych. W teorii jest to dobry pomysł, jednak w praktyce prowadzi do spadku wydajności aplikacji. Powstało kilka interfejsów API do tworzenia odwzorowań obiektowo-relacyjnych, np. Hibernate, iBatis, Cayenne i Toplink. Interfejsy te miały rozwiązać problemy związane z ziarnami encyjnymi. W Javie EE 5 zrezygnowano z ziaren encyjnych na rzecz interfejsu JPA. Twórcy tego interfejsu wykorzystali pomysły z kilku narzędzi do tworzenia odwzorowań obiektowo-relacyjnych i wbudowali je w standard. Jak przekonasz się w tym rozdziale, środowisko NetBeans udostępnia kilka mechanizmów, dzięki którym programowanie z wykorzystaniem interfejsu JPA jest bardzo proste. W tym rozdziale opisano następujące zagadnienia: tworzenie pierwszej encji JPA; komunikowanie się z encjami JPA za pomocą narzędzia EntityManager;
Java EE 6. Tworzenie aplikacji w NetBeans 7
generowanie formularzy na stronach JSF za pomocą encji JPA; generowanie encji JPA na podstawie schematu istniejącej bazy danych; kwerendy nazwane JPA i język JPQL; relacje między encjami; generowanie kompletnych aplikacji JSF na podstawie encji JPA.
Tworzenie pierwszej encji JPA Encje JPA to klasy Javy, których pola są utrwalane w bazie danych przez interfejs JPA. Encje JPA to obiekty POJO (ang. plain old Java objects, czyli zwykłe obiekty Javy), dlatego nie muszą dziedziczyć po konkretnej klasie bazowej ani implementować określonego interfejsu. Klasę Javy można ustawić jako encję JPA, dodając adnotację @Entity. Aby utworzyć i przetestować pierwszą encję JPA, najpierw zbudujesz nową aplikację sieciową za pomocą platformy JavaServer Faces. Nazwij ją jpaweb i, jak we wszystkich przykładach, zastosuj wbudowany serwer aplikacji GlassFish. Instrukcje dotyczące tworzenia nowych projektów JSF znajdziesz w rozdziale 4.
Aby utworzyć nową encję JPA, w oknie dialogowym New File otwórz kategorię Persistence i wybierz Entity Class jako typ pliku (rysunek 6.1).
Rysunek 6.1. Tworzenie klasy encji JPA
186
Rozdział 6. • Interakcja z bazami danych
W środowisku NetBeans uruchomi się wtedy kreator New Entity Class (rysunek 6.2).
Rysunek 6.2. Kreator klas encji JPA
Na tym etapie należy podać wartości pól Class Name i Package (tu wpisz wartości Customer i com.ensode.jpaweb). W projektach używających encji JPA potrzebne są jednostki odpowiedzialne za utrwalanie danych. Takie jednostki definiuje się w pliku persistence.xml. Gdy utworzysz pierwszą encję JPA w projekcie, środowisko NetBeans wykryje, że taki plik nie istnieje, i automatycznie zaznaczy pole wyboru Create Persistence Unit. W następnym kroku kreatora można wprowadzić informacje potrzebne do utworzenia jednostki odpowiedzialnej za utrwalanie danych (rysunek 6.3). Kreator Create Persistence Unit proponuje nazwę tworzonej jednostki. W większości sytuacji można bezpiecznie zaakceptować wartość domyślną. JPA to specyfikacja, dla której istnieje zestaw implementacji. Środowisko NetBeans obsługuje kilka z nich, w tym EclipseLink, Toplink Essentials, Hibernate, KODO i OpenJPA. Ponieważ we wbudowanym serwerze aplikacji GlassFish domyślną implementacją JPA jest EclipseLink, to przy instalowaniu aplikacji na tym serwerze warto pozostawić w polu Persistence Provider wartość domyślną odpowiadającą tej implementacji. Zanim będzie można komunikować się z bazą danych z poziomu aplikacji Javy EE, trzeba na serwerze aplikacji utworzyć pulę połączeń i źródło danych.
187
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 6.3. Konfigurowanie jednostki odpowiedzialnej za utrwalanie danych
Pula połączeń zawiera informacje (np. nazwę serwera, numer portu i dane uwierzytelniające), które umożliwiają nawiązywanie połączeń z bazą danych. Zaletą używania puli połączeń zamiast bezpośredniego otwierania połączeń JDBC z bazą danych jest to, że połączenia z puli nigdy nie są zamykane. Serwer tylko przypisuje je aplikacjom, które ich potrzebują. Prowadzi to do wyższej wydajności, ponieważ otwieranie i zamykanie połączeń z bazą danych to czasochłonne operacje. Źródła danych umożliwiają uzyskanie połączenia z puli dzięki pobraniu obiektu typu javax.sql. DataSource na podstawie identyfikatora JNDI. Następnie można wywołać metodę get Connection() takiego obiektu, aby uzyskać połączenie. Przy stosowaniu interfejsu JPA nie trzeba bezpośrednio pobierać referencji do źródła danych (interfejs JPA automatycznie wykonuje tego typu operacje), należy jednak wskazać źródło danych używane w jednostce odpowiedzialnej za utrwalanie danych. W środowisku NetBeans dostępnych jest kilka wstępnie skonfigurowanych źródeł danych i pul połączeń. W aplikacji można wykorzystać jeden z tych zasobów, jednak środowisko NetBeans umożliwia też tworzenie ich w locie i właśnie tę technikę wykorzystasz w omawianym przykładzie. Aby utworzyć nowe źródło danych, należy wybrać pozycję New Data Source… w polu kombi Data Source (rysunek 6.4).
188
Rozdział 6. • Interakcja z bazami danych
Rysunek 6.4. Tworzenie źródła danych
Źródło danych musi się komunikować z pulą połączeń z bazą danych. W środowisku NetBeans dostępnych jest kilka wstępnie skonfigurowanych pul połączeń, jednak (podobnie jak w przypadku źródeł danych) można też tworzyć nowe pule na żądanie. W tym celu należy wybrać pozycję New Database Connection… w polu kombi Database Connection (rysunek 6.5).
Rysunek 6.5. Tworzenie nowej puli połączeń
W środowisku NetBeans dostępne są wbudowane sterowniki JDBC dla kilku systemów RDBMS, np. dla systemów JavaDB, HSQLDB, MySQL, PostgreSQL i Oracle. System JavaDB jest udostępniany razem z serwerem GlassFish i środowiskiem NetBeans, dlatego jest używany w tym przykładzie (rysunek 6.6). Pozwala to uniknąć konieczności instalowania zewnętrznego systemu RDBMS.
189
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 6.6. Konfigurowanie połączenia Jeśli dla danego systemu RDBMS nie ma wbudowanej obsługi, trzeba pobrać odpowiedni sterownik JDBC i wskazać środowisku NetBeans jego lokalizację po wybraniu pozycji New Driver w polu kombi Name. Następnie można przejść do katalogu z plikiem JAR zawierającym potrzebny sterownik JDBC. Szczegółowe informacje znajdziesz w dokumentacji używanego systemu RDBMS.
System JavaDB jest zainstalowany w stacji roboczej, na której tworzona jest aplikacja, dlatego używana nazwa serwera to localhost. System JavaDB domyślnie oczekuje na dane w porcie 1527, więc właśnie ten port podany jest w adresie URL. Aplikacja ma łączyć się z bazą jpaintro, dlatego podana jest nazwa tej bazy. Ponieważ baza ta jeszcze nie istnieje, do systemu JavaDB należy przekazać atrybut create=true. Powoduje on, że baza zostanie utworzona, jeśli jeszcze nie istnieje. Podane nazwa użytkownika i hasło są automatycznie dodawane do nowo utworzonego schematu. Każda baza danych w systemie JavaDB zawiera schemat o nazwie APP (rysunek 6.7), ponieważ każdy użytkownik domyślnie używa schematu zgodnego z nazwą używaną przy logowaniu. Najłatwiej skonfigurować takie rozwiązanie, tworząc użytkownika o nazwie APP i ustawiając dla niego hasło. Po utworzeniu nowego źródła danych i puli połączeń można przejść do konfigurowania jednostki odpowiedzialnej za utrwalanie danych (rysunek 6.8).
190
Rozdział 6. • Interakcja z bazami danych
Rysunek 6.7. Schemat APP jest dostępny w każdej bazie w systemie JavaDB
Rysunek 6.8. Konfigurowanie jednostki odpowiedzialnej za utrwalanie danych
191
Java EE 6. Tworzenie aplikacji w NetBeans 7
Warto pozostawić zaznaczone pole wyboru Use Java Transaction APIs. To sprawia, że implementacja interfejsu JPA będzie korzystać z interfejsu JTA (ang. Java Transaction API) i umożliwi serwerowi aplikacji zarządzanie transakcjami. Jeśli usuniesz zaznaczenie tego pola, będziesz musiał ręcznie napisać kod do zarządzania transakcjami. W większości implementacji interfejsu JPA można zdefiniować strategię generowania tabel. Można nakazać takiej implementacji tworzenie odpowiadającym encjom tabel w momencie instalowania aplikacji, usuwanie tabel i późniejsze odtwarzanie ich przy instalowaniu aplikacji lub pominięcie tworzenia tabel. W środowisku NetBeans można ustawić strategię generowania tabel dzięki kliknięciu odpowiedniej wartości w grupie przycisków opcji Table Generation Strategy. W czasie pracy z nowo utworzoną rozwojową bazą danych warto wybrać strategię Drop and Create. Dzięki temu można swobodnie dodawać i usuwać nazwy pól w encji JPA oraz zmieniać ich nazwy bez konieczności modyfikowania schematu bazy danych. Przy stosowaniu tej strategii tabele w schemacie bazy danych są usuwane i odtwarzane przy każdym instalowaniu aplikacji, dlatego wcześniej utrwalone dane są tracone.
Po utworzeniu nowego źródła danych, nawiązaniu połączenia z bazą i utworzeniu jednostki odpowiedzialnej za utrwalanie danych można przejść do tworzenia nowej encji JPA. Aby to zrobić, wystarczy kliknąć przycisk Finish. Wtedy środowisko NetBeans wygeneruje kod źródłowy encji JPA. W JPA można powiązać pole klucza głównego w encji JPA z kolumną dowolnego typu (VARCHAR, NUMBER itd.). Najlepiej stosować liczbowy sztuczny klucz główny, czyli klucz główny, który pełni wyłącznie funkcję identyfikatora i nie ma w aplikacji znaczenia biznesowego. Pozostawienie domyślnego typu klucza głównego (długich liczb całkowitych) zapewnia duży zakres wartości klucza głównego w encjach. package com.ensode.jpaweb; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Customer implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id;
192
Rozdział 6. • Interakcja z bazami danych
} // Pozostałe wygenerowane metody (equals(), hashCode(), toString()) // zostały pominięte z uwagi na konieczność zachowania zwięzłości tekstu }
Jak widać, encja JPA to standardowy obiekt Javy — nie musi dziedziczyć po żadnej specjalnej klasie ani implementować określonego interfejsu. Tym, co odróżnia encje JPA od innych obiektów Javy, są charakterystyczne dla JPA adnotacje. Adnotacja @Entity służy do określania, że dana klasa jest encją JPA. Każdy obiekt, który ma zostać utrwalony w bazie danych za pomocą interfejsu JPA, musi mieć taką adnotację. Adnotacja @Id określa, które pole w encji JPA pełni funkcję klucza głównego. Klucz główny to niepowtarzalny identyfikator encji. Dwie encje nie mogą mieć tej samej wartości w polu klucza głównego. Tę adnotację można umieścić bezpośrednio nad getterem klucza głównego (to podejście stosuje kreator w środowisku NetBeans). Można też podać tę adnotację bezpośrednio nad deklaracją pola. @Entity i @Id to dwie podstawowe adnotacje, które trzeba umieścić w klasie używanej jako encja JPA. Interfejs JPA umożliwia automatyczne generowanie kluczy głównych. Aby móc wykorzystać ten mechanizm, należy zastosować adnotację @GeneratedValue. Jak widać, środowisko NetBeans generuje encje JPA z tą adnotacją. Służy ona do określania strategii generowania kluczy głównych. Wszystkie możliwe strategie znajdziesz w tabeli 6.1. Tabela 6.1. Strategie generowania kluczy głównych Strategia generowania kluczy głównych
Opis
GenerationType.AUTO
Powoduje, że dostawca odpowiedzialny za utrwalanie danych automatycznie określa strategię generowania kluczy głównych. Ta opcja jest używana domyślnie, jeśli nie określono strategii.
GenerationType.IDENTITY
Powoduje, że do generowania wartości kluczy głównych należy wykorzystać kolumnę z identyfikatorem z tabeli bazy danych odpowiadającej danej encji JPA.
GenerationType.SEQUENCE
Powoduje, że do generowania wartości kluczy głównych w encji należy wykorzystać sekwencję z bazy danych.
GenerationType.TABLE
Powoduje, że do generowania wartości kluczy głównych w encji należy wykorzystać tabelę z bazy danych.
Zwykle strategia GenerationType.AUTO działa poprawnie, dlatego jest używana prawie zawsze. Z tego powodu strategia ta jest najczęściej wybierana w kreatorze New Entity Class. Przy stosowaniu strategii opartych na sekwencji lub tabeli konieczne może być wskazanie konkretnej sekwencji albo tabeli używanej do generowania kluczy głównych. Elementy te można wskazać za pomocą adnotacji @SequenceGenerator i @TableGenerator. Szczegółowe informacje znajdziesz w dokumentacji JavaDoc Javy EE 6: http://download.oracle.com/javaee/6/api/.
193
Java EE 6. Tworzenie aplikacji w NetBeans 7
Dodawanie utrwalanych pól do encji Na tym etapie encja obejmuje jedno pole (klucz główny) i trzeba przyznać, że nie jest zbyt przydatna. Trzeba dodać kilka pól przeznaczonych do utrwalania w bazie danych. package com.ensode.jpaweb; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Customer implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
Ta zmodyfikowana wersja encji JPA zawiera dwa nowe pola utrwalane w bazie danych. Pole firstName posłuży do przechowywania imienia użytkownika, natomiast pole lastName — do przechowywania nazwiska. W encjach JPA należy stosować standardowe konwencje związane z pisaniem ziaren JavaBeans. Trzeba udostępnić publiczny konstruktor bezargumentowy (jest on automatycznie generowany przez kompilator Javy, jeśli programista nie utworzył innych konstruktorów), a wszystkie pola muszą być prywatne i dostępne za pomocą getterów i setterów.
194
Rozdział 6. • Interakcja z bazami danych
Automatyczne generowanie getterów i setterów W środowisku NetBeans gettery i settery można generować automatycznie. Wystarczy zadeklarować nowe pola w standardowy sposób, a następnie wcisnąć skrót klawiaturowy przeznaczony do wstawiania kodu (domyślnie jest to skrót Alt+Insert) i wybrać opcję Getter and Setter w wyświetlonym oknie. Potem kliknij pole wyboru obok nazwy klasy, aby zaznaczyć wszystkie pola, i kliknij przycisk Generate.
Zanim będzie można wykorzystać interfejs JPA do utrwalania pól z encji w bazie danych, trzeba napisać dodatkowy kod.
Tworzenie obiektów DAO Przy pisaniu kodu komunikującego się z bazą danych warto przestrzegać wzorca projektowego DAO (ang. Data Access Object). Wzorzec ten polega na umieszczaniu wszystkich mechanizmów dostępu do bazy danych w klasach DAO. Zaletą tego podejścia jest jasne rozdzielenie zadań. Dzięki temu w innych warstwach aplikacji (np. w kodzie interfejsu lub logice biznesowej) nie trzeba umieszczać kodu odpowiedzialnego za utrwalanie danych. Środowisko NetBeans pomaga generować klasy kontrolerów JPA na podstawie istniejących encji. Tworzone w ten sposób klasy kontrolerów JPA są zgodne ze wzorcem projektowym DAO. Aby wygenerować taką klasę, w oknie dialogowym New File otwórz kategorię Persistence i wybierz typ pliku Classes from Entity Classes (rysunek 6.9).
Rysunek 6.9. Tworzenie klas kontrolerów na podstawie klas encji
195
Java EE 6. Tworzenie aplikacji w NetBeans 7
Na następnej stronie kreatora należy wybrać klasy encji, dla których kreator ma wygenerować klasy kontrolerów JPA (rysunek 6.10).
Rysunek 6.10. Wybieranie klasy encji
Następnie należy wskazać projekt i pakiet dla klas kontrolerów JPA (rysunek 6.11). Gdy klikniesz przycisk Finish, klasa kontrolera JPA zostanie z powodzeniem wygenerowana. package com.ensode.jpaweb; import com.ensode.jpaweb.exceptions.NonexistentEntityException; import java.io.Serializable; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Query; import javax.persistence.EntityNotFoundException; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; import javax.transaction.UserTransaction; public class CustomerJpaController implements Serializable { public CustomerJpaController(UserTransaction utx, EntityManagerFactory emf) { this.utx = utx;
196
Rozdział 6. • Interakcja z bazami danych
Rysunek 6.11. Konfigurowanie klasy kontrolera this.emf = emf; } private UserTransaction utx = null; private EntityManagerFactory emf = null; public EntityManager getEntityManager() { return emf.createEntityManager(); } public void create(Customer customer) { EntityManager em = null; try { em = getEntityManager(); em.getTransaction().begin(); em.persist(customer); em.getTransaction().commit(); } finally { if (em != null) { em.close(); } } } public void edit(Customer customer) throws NonexistentEntityException, Exception { EntityManager em = null; try {
197
Java EE 6. Tworzenie aplikacji w NetBeans 7
em = getEntityManager(); em.getTransaction().begin(); customer = em.merge(customer); em.getTransaction().commit(); } catch (Exception ex) { String msg = ex.getLocalizedMessage(); if (msg == null || msg.length() == 0) { Long id = customer.getId(); if (findCustomer(id) == null) { throw new NonexistentEntityException( "Klient o identyfikatorze " + id + " nie istnieje."); } } throw ex; } finally { if (em != null) { em.close(); } } } } public void destroy(Long id) throws NonexistentEntityException { EntityManager em = null; try { em = getEntityManager(); em.getTransaction().begin(); Customer customer; try { customer = em.getReference(Customer.class, id); customer.getId(); } catch (EntityNotFoundException enfe) { throw new NonexistentEntityException("Klient o identyfikatorze " + id + " nie istnieje.", enfe); } em.remove(customer); em.getTransaction().commit(); } finally { if (em != null) { em.close(); } } } public List findCustomerEntities() { return findCustomerEntities(true, -1, -1); } public List findCustomerEntities(int maxResults, int firstResult) { return findCustomerEntities(false, maxResults, firstResult);
198
Rozdział 6. • Interakcja z bazami danych
} private List findCustomerEntities(boolean all, int maxResults, int firstResult) { EntityManager em = getEntityManager(); try { CriteriaQuery cq = em.getCriteriaBuilder().createQuery(); cq.select(cq.from(Customer.class)); Query q = em.createQuery(cq); if (!all) { q.setMaxResults(maxResults); q.setFirstResult(firstResult); } return q.getResultList(); } finally { em.close(); } } public Customer findCustomer(Long id) { EntityManager em = getEntityManager(); try { return em.find(Customer.class, id); } finally { em.close(); } } public int getCustomerCount() { EntityManager em = getEntityManager(); try { CriteriaQuery cq = em.getCriteriaBuilder().createQuery(); Root rt = cq.from(Customer.class); cq.select(em.getCriteriaBuilder().count(rt)); Query q = em.createQuery(cq); return ((Long) q.getSingleResult()).intValue(); } finally { em.close(); } } }
Jak widać, środowisko NetBeans generuje metody do tworzenia, wczytywania, aktualizowania i usuwania encji JPA. Metoda służąca do tworzenia nowych encji to create(). Przyjmuje ona jeden argument — egzemplarz encji JPA. Metoda ta wywołuje metodę persist() klasy EntityManager, która odpowiada za utrwalanie danych z encji JPA w bazie. Na potrzeby odczytu generowanych jest kilka metod. Metoda findCustomer() przyjmuje jeden argument (klucz główny pobieranej encji JPA), a następnie wywołuje metodę find() klasy EntityManager, aby pobrać dane z bazy i zwrócić egzemplarz encji JPA. Generowanych jest
199
Java EE 6. Tworzenie aplikacji w NetBeans 7
kilka przeciążonych wersji metody findCustomerEntities(). Metody te umożliwiają pobranie z bazy danych więcej niż jednej encji JPA. Wersja metody odpowiedzialna za samo pobieranie danych ma następującą sygnaturę: private List findCustomerEntities(boolean all, int maxResults, int firstResult)
Pierwszym parametrem tej metody jest wartość logiczna, która pozwala określić, czy metoda ma pobrać wszystkie wartości z bazy. Drugi parametr pozwala określić maksymalną liczbę pobieranych wyników, a trzeci — pierwszy pobierany wynik. W metodzie tej używany jest wprowadzony w JPA 2.0 interfejs API Criteria. Pozwala on na programowe tworzenie kwerend. Jeśli wartość parametru all to false, metoda ustawia maksymalną liczbę wyników i pierwszy pobierany wynik, przekazując odpowiednie parametry do metod setMaxResults() i setFirst Result() obiektu kwerendy. Metoda do aktualizowania istniejących encji to edit(). Przyjmuje ona jeden parametr — egzemplarz encji JPA. Metoda ta wywołuje metodę merge() klasy EntityManager, co powoduje zaktualizowanie danych w bazie na podstawie danych z encji JPA przekazanej jako parametr. Metoda przeznaczona do usuwania encji to destroy(). Przyjmuje ona jeden parametr — klucz główny obiektu przeznaczonego do usunięcia. Metoda ta najpierw sprawdza, czy obiekt istnieje w bazie. Jeśli nie, zgłasza wyjątek. Jeżeli obiekt istnieje, usuwa odpowiedni wiersz z bazy, wywołując metodę remove() klasy EntityManager. Na tym etapie dostępny jest już cały kod potrzebny do utrwalenia właściwości encji w bazie danych. Teraz, aby wykonywać podstawowe operacje bazodanowe na encji JPA, wystarczy wywoływać w kodzie metody wygenerowanego kontrolera JPA.
Automatyczne generowanie encji JPA W wielu projektach używany jest schemat istniejącej bazy danych przygotowany przez administratora bazy. Środowisko NetBeans może generować encje JPA na podstawie schematów istniejących baz danych, co pozwala programistom uniknąć długiej i potencjalnie żmudnej pracy. W tym podrozdziale używany jest niestandardowy schemat bazy danych. Aby utworzyć schemat, trzeba uruchomić skrypt SQL i zapełnić tabele bazy danych. W tym celu należy otworzyć okno Services, kliknąć prawym przyciskiem myszy węzeł JavaDB, a następnie wybrać opcję Create Database… (rysunek 6.12). Następnie trzeba w kreatorze Create Java DB Database podać informacje o bazie danych (rysunek 6.13).
200
Rozdział 6. • Interakcja z bazami danych
Rysunek 6.12. Tworzenie nowej bazy danych
Rysunek 6.13. Podawanie informacji o bazie danych
Na tym etapie można otworzyć skrypt SQL. W tym celu wybierz opcję File/Open File…, a następnie przejdź do katalogu ze skryptem na dysku twardym i otwórz skrypt. Nazwa potrzebnego skryptu to create_populate_tables.sql (rysunek 6.14). Znajdziesz go w kodzie źródłowym przeznaczonym dla tego rozdziału.
Po otwarciu skryptu SQL należy w polu kombi Connection wybrać nowo utworzone połączenie, a następnie kliknąć ikonę z rysunku 6.15, aby uruchomić skrypt. W bazie danych pojawi się zestaw tabel (rysunek 6.16). Aby wygenerować encje JPA na podstawie istniejącego schematu (takiego jak przygotowany przed chwilą), trzeba utworzyć nowy projekt, kliknąć go prawym przyciskiem myszy, a następnie w oknie dialogowym New File wybrać kategorię Persistence i kliknąć typ pliku Entity Classes from Database (rysunek 6.17). Środowisko NetBeans umożliwia generowanie encji JPA w niemal dowolnym projekcie Javy. Tu używany jest projekt aplikacji sieciowej.
201
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 6.14. Zawartość skryptu create_populate_tables.sql
Rysunek 6.15. Kliknij tę ikonę, aby uruchomić skrypt
Rysunek 6.16. Nowe tabele bazy danych
202
Rozdział 6. • Interakcja z bazami danych
Rysunek 6.17. Generowanie encji JPA na podstawie schematu bazy danych
Teraz można wybrać istniejące źródło danych lub (jak w poprzednim przykładzie) utworzyć nowe w locie. Tu utwórz nowe źródło danych, a następnie wybierz połączenie z bazą przygotowane we wcześniejszej części rozdziału. Po utworzeniu lub wybraniu źródła danych należy wskazać jedną lub kilka tabel, które mają posłużyć do generowania encji JPA (rysunek 6.18). Jeśli chcesz utworzyć encje JPA dla wszystkich tabel, kliknij przycisk Add All>>. Gdy klikniesz przycisk Next>, środowisko NetBeans umożliwi zmianę nazw wygenerowanych klas (choć nazwy domyślne są zwykle sensowne), co przedstawia rysunek 6.19. Należy też określić pakiet klas i warto zaznaczyć pole wyboru Generate Named Query Annotations for Persistent Fields. Opcjonalnie można wygenerować adnotacje JAXB (ang. Java API for XML Binding) i utworzyć jednostkę odpowiedzialną za utrwalanie danych. Szczegółowe omówienie kwerend nazwanych znajdziesz w dalszym punkcie.
Na następnym ekranie kreatora można określić, w jaki sposób domyślnie pobierane mają być powiązane encje (metodą zachłanną lub leniwą). Tu używane są ustawienia domyślne — relacje „jeden do jednego” i „wiele do jednego” są pobierane w sposób zachłanny, a relacje „jeden do wielu” i „wiele do wielu” w sposób leniwy.
203
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 6.18. Wybieranie tabel bazy danych wiązanych z encją JPA
Rysunek 6.19. W tym oknie można określić nazwy i lokalizację klas encji
204
Rozdział 6. • Interakcja z bazami danych
Ponadto można zaznaczyć, jakiego typu kolekcja ma być używana dla strony „wiele” w relacjach „jeden do wielu” i „wiele do wielu” (rysunek 6.20). Domyślnie używana jest kolekcja java.util.Collection. Inne poprawne wartości to java.util.List i java.util.Set.
Rysunek 6.20. Konfigurowanie odwzorowań
Jeśli zaznaczysz pole wyboru Fully Qualified Database Table Names, katalog i schemat tabeli zostaną odwzorowane na adnotację @Table w każdej generowanej encji. Zaznaczenie pola wyboru Attributes for Regenerating Tables powoduje, że do wygenerowanej adnotacji @Column dodawane są atrybuty: length (określa maksymalną długość wartości dozwoloną w kolumnie), nullable (informuje, że w kolumnie dopuszczalne są wartości null) oraz precision i scale (określają precyzję i skalę wartości dziesiętnych). Zaznaczenie wspomnianego pola powoduje też dodanie do wygenerowanej adnotacji @Table atrybutu uniqueContstraints. Pozwala on określić dla tabeli ograniczenia dotyczące niepowtarzalności, jeśli są one potrzebne. Gdy klikniesz przycisk Finish, środowisko NetBeans wygeneruje encje JPA dla wszystkich tabel z bazy danych. Używana tu baza zawiera tabelę CUSTOMER. Przyjrzyj się wygenerowanej na jej podstawie encji JPA Customer. package com.ensode.jpa; // Operacje importu pominięto ze względu na konieczność zachowania zwięzłości tekstu @Entity @Table(name = "CUSTOMER") @NamedQueries({ @NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c"), @NamedQuery(name = "Customer.findByCustomerId",
205
Java EE 6. Tworzenie aplikacji w NetBeans 7
query = "SELECT c FROM Customer c WHERE c.customerId = :customerId"), @NamedQuery(name = "Customer.findByFirstName", query = "SELECT c FROM Customer c WHERE c.firstName = :firstName"), @NamedQuery(name = "Customer.findByMiddleName", query = "SELECT c FROM Customer c WHERE c.middleName = :middleName"), @NamedQuery(name = "Customer.findByLastName", query = "SELECT c FROM Customer c WHERE c.lastName = :lastName"), @NamedQuery(name = "Customer.findByEmail", query = "SELECT c FROM Customer c WHERE c.email = :email")}) public class Customer implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @Column(name = "CUSTOMER_ID") private Integer customerId; @Size(max = 20) @Column(name = "FIRST_NAME") private String firstName; @Size(max = 20) @Column(name = "MIDDLE_NAME") private String middleName; @Size(max = 20) @Column(name = "LAST_NAME") private String lastName; //@Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~]+)*@(?:[a-z0-9](?:[a-z0-9-]* // [a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Adres e-mail jest nieprawidłowy") // Jeśli pole zawiera adres e-mail, rozważ zastosowanie powyższej adnotacji w celu // sprawdzania jego poprawności @Size(max = 30) @Column(name = "EMAIL") private String email; @OneToMany(mappedBy = "customerId") private Collection customerOrderCollection; @OneToMany(mappedBy = "customerId") private Collection addressCollection; @OneToMany(mappedBy = "customerId") private Collection telephoneCollection; // Konstruktory, gettery, settery i inne automatycznie wygenerowane metody (equals(), // hashCode() i toString()) zostały pominięte }
Jak widać, środowisko NetBeans generuje klasę z adnotacją @Entity, co sprawia, że klasa jest traktowana jak encja JPA. Zauważ, że środowisko NetBeans automatycznie dodało do jednego z pól adnotację @Id. Zrobiło to na podstawie ograniczenia klucza głównego z tabeli użytej do
206
Rozdział 6. • Interakcja z bazami danych
wygenerowania tej encji JPA. Warto zwrócić uwagę na to, że jeśli nie jest używana strategia generowania kluczy głównych, trzeba samodzielnie określić wartości klucza głównego lub ręcznie dodać adnotację @GeneratedValue. Przyjrzyj się adnotacji @Table. Jest to opcjonalna adnotacja określająca, która tabela została odwzorowana na daną encję JPA. Jeśli pominiesz tę adnotację, encja będzie odpowiadać tabeli o tej samej nazwie co encja (wielkość znaków nie ma tu znaczenia). Tu adnotacja @Table jest zbędna, jednak czasem okazuje się przydatna. Na przykład w schematach niektórych baz danych nazwy tabel są podawane w liczbie mnogiej (CUSTOMERS), natomiast nazwy encji powinny być podane w liczbie pojedynczej (Customer). Ponadto standardową konwencją nazewniczą dla tabel baz danych jest rozdzielanie odrębnych słów podkreśleniem (np. CUSTOMER_ORDER), natomiast w Javie stosuje się zwykle połączone słowa rozpoczynające się wielką literą (np. CustomerOrder). Adnotacja @Table pozwala zastosować się do standardów nazewniczych zarówno w relacyjnej bazie danych, jak i w Javie.
Kwerendy nazwane i język JPQL Dalej znajduje się adnotacja @NamedQueries (jest ona generowana tylko wtedy, gdy w kreatorze New Entity Classes from Database programista zaznaczył pole wyboru Generate Named Query Annotations for Persistent Fields). Adnotacja ta ma atrybut value, którego nazwę można w kodzie pominąć, ponieważ jest to jedyny atrybut tej adnotacji. Wartością atrybutu jest tablica adnotacji @NamedQuery. Adnotacje te mają atrybut name, pozwalający określić logiczną nazwę kwerendy (zwyczajowo w nazwach kwerend pojawiają się nazwy używanych encji JPA; jak widać w wygenerowanym kodzie, kreator New Entity Classes from Database działa zgodnie z tą konwencją), i atrybut query, który służy do definiowania kwerendy w języku JPQL (ang. Java Persistence Query Language) uruchamianej za pomocą kwerendy nazwanej. JPQL to charakterystyczny dla JPA język kwerend o składni podobnej do SQL-a. Kreator New Entity Classes from Database generuje kwerendę JPQL-a dla każdego pola encji. Po wykonaniu kwerendy zwracany jest obiekt typu List z wszystkimi egzemplarzami encji pasującymi do kryteriów ustawionych w kwerendzie. Poniższy fragment kodu ilustruje ten proces. import import import public public {
java.util.List; javax.persistence.EntityManager; javax.persistence.Query; class CustomerDAO { List findCustomerByLastName(String someLastName) // Kod związany z obiektem typu EntityManager pominięto Query query = em.createNamedQuery("Customer.findByLastName"); query.setParameter("lastName", someLastName); List resultList = query.getResultList(); return resultList;
} }
207
Java EE 6. Tworzenie aplikacji w NetBeans 7
Tu obiekt DAO zawiera metodę zwracającą listę encji Customer z danymi klientów, których nazwisko pasuje do wartości parametru metody. Aby zaimplementować to rozwiązanie, trzeba utworzyć obiekt typu javax.persistence.Query (tak jak w pokazanym fragmencie kodu). W tym celu można wywołać metodę createNamedQuery() klasy EntityManager i przekazać do niej jako parametr nazwę kwerendy (zdefiniowaną w adnotacji @NamedQuery). Zauważ, że kwerendy nazwane generowane przez środowisko NetBeans obejmują łańcuchy znaków poprzedzone średnikiem. Te łańcuchy znaków to parametry nazwane. Są to miejsca w kodzie zastępowane później odpowiednimi wartościami. W przykładowym kodzie parametr nazwany lastName jest ustawiany w kwerendzie JPQL-a za pomocą przekazanego do metody argumentu someLastName. Po zapełnieniu wszystkich parametrów kwerendy można pobrać obiekt typu List z wszystkimi pasującymi encjami. W tym celu należy wywołać metodę getResultList() obiektu typu Query. Pora wrócić do wygenerowanej encji JPA. Zwróć uwagę na to, że kreator automatycznie dodał adnotację @Id dla pola odpowiadającego kluczowi głównemu tabeli. Ponadto każde pole ma adnotację @Column, co pozwala zastosować standardowe konwencje nazewnicze zarówno w relacyjnej bazie danych, jak i w Javie. Adnotacja ta nie tylko pozwala wskazać, którym kolumnom odpowiadają poszczególne pola, ale też ma atrybut nullable, umożliwiający określenie, czy w kolumnie dozwolone są wartości Null. W polu klucza głównego encji kreator automatycznie ustawia wartość atrybutu nullable na false.
Sprawdzanie poprawności ziaren Sprawdzanie poprawności ziaren to mechanizm utworzony na podstawie dokumentu JSR (ang. Java Specification Request) 303. Jest to nowość w specyfikacji Javy EE. Sprawdzanie poprawności ziaren jest zaimplementowane za pomocą zbioru adnotacji z pakietu javax. validation. Kreator encji JPA w środowisku NetBeans w pełni obsługuje sprawdzanie poprawności ziaren i dodaje związane z tym mechanizmem adnotacje do odpowiednich pól. Robi to na podstawie definicji tabel używanych do generowania encji. W encji Customer znajduje się kilka adnotacji związanych ze sprawdzaniem poprawności ziaren. Pole customerId ma adnotację @NotNull, która — jak wskazuje na to nazwa — uniemożliwia zapisanie w tym polu wartości Null. Kilka pól z encji Customer ma adnotację @Size. Ta adnotacja pozwala określić maksymalną liczbę znaków zapisywanych we właściwości ziarna. Także te informacje kreator ze środowiska NetBeans pobiera z tabel używanych do generowania encji. @Pattern to następna adnotacja używana do sprawdzania poprawności ziaren. Pozwala ona upewnić się, że wartość pola z adnotacją pasuje do podanego wyrażenia regularnego.
208
Rozdział 6. • Interakcja z bazami danych
Zauważ, że bezpośrednio nad właściwością email encji Customer kreator umieścił adnotację @Pattern i oznaczył ją jako komentarz. Wynika to z tego, że kreator wykrył, iż nazwa kolumny w tabeli to EMAIL, i przyjął, że kolumna ta może służyć do przechowywania adresów e-mail. Dlatego dodał adnotację z wyrażeniem regularnym sprawdzającym poprawność adresów e-mail, jednak ponieważ nie miał pewności, że kolumna ta rzeczywiście jest przeznaczona na adresy e-mail, umieścił tę adnotację w komentarzu. Opisywana właściwość faktycznie służy do przechowywania adresów e-mail, dlatego należy usunąć symbol komentarza z automatycznie wygenerowanego wiersza z adnotacją.
Relacje między encjami Istnieje kilka adnotacji, które można wykorzystać w encjach JPA do definiowania relacji. W przedstawionej wcześniej encji Customer widać, że kreator wykrył w tabeli CUSTOMER kilka relacji „jeden do wielu” i automatycznie dodał adnotację @OneToMany, aby zdefiniować te relacje w encji. Zauważ, że każde pole z adnotacją @OneToMany jest typu java.util.Collection. Encja Customer znajduje się po stronie „jeden” relacji, ponieważ z klientem można powiązać wiele zamówień, adresów (mieszkania, e-mail) i numerów telefonu (domowy, służbowy, komórkowy itd.). Warto zwrócić uwagę, że kreator używa typów generycznych do określania typu obiektów, jakie można zapisywać w poszczególnych kolekcjach. Obiekty z tych kolekcji to encje JPA odpowiadające tabelom ze schematu bazy danych. Adnotacja @OneToMany ma atrybut mappedBy. Jest on niezbędny, ponieważ każda z relacji jest dwukierunkowa (można uzyskać dostęp do wszystkich adresów klienta, a na podstawie adresu można ustalić odpowiadającego mu klienta). Wartość tego atrybutu musi pasować do nazwy pola po drugiej stronie relacji. Przyjrzyj się encji Address, aby zrozumieć drugą stronę relacji klient-adres. package com.ensode.jpa; // Operacje importu pominięto @Entity @Table(name = "ADDRESS") @NamedQueries({ @NamedQuery(name = "Address.findAll", query = "SELECT a FROM Address a"), @NamedQuery(name = "Address.findByAddressId", query = "SELECT a FROM Address a WHERE a.addressId = :addressId"), @NamedQuery(name = "Address.findByAddrLine1", query = "SELECT a FROM Address a WHERE a.addrLine1 = :addrLine1"), @NamedQuery(name = "Address.findByAddrLine2", query = "SELECT a FROM Address a WHERE a.addrLine2 = :addrLine2"), @NamedQuery(name = "Address.findByCity", query = "SELECT a FROM Address a WHERE a.city = :city"), @NamedQuery(name = "Address.findByZip", query = "SELECT a FROM Address a WHERE a.zip = :zip")})
209
Java EE 6. Tworzenie aplikacji w NetBeans 7
public class Address implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @Column(name = "ADDRESS_ID") private Integer addressId; @Size(max = 100) @Column(name = "ADDR_LINE_1") private String addrLine1; @Size(max = 100) @Column(name = "ADDR_LINE_2") private String addrLine2; @Size(max = 100) @Column(name = "CITY") private String city; @Size(max = 5) @Column(name = "ZIP") private String zip; @JoinColumn(name = "US_STATE_ID", referencedColumnName = "US_STATE_ID") @ManyToOne private UsState usStateId; @JoinColumn(name = "CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID") @ManyToOne private Customer customerId; @JoinColumn(name = "ADDRESS_TYPE_ID", referencedColumnName = "ADDRESS_TYPE_ID") @ManyToOne private AddressType addressTypeId; // Konstruktory, gettery, settery i metody equals(), hashCode() oraz // toString() pominięto }
Zauważ, że encja Address ma pole customerId. Pole to jest typu Customer (czyli typu omawianej wcześniej encji). Należy przyznać, że lepszą nazwą pola byłoby customer, jednak kreator New Entity Classes from Database tworzy nazwy pól na podstawie nazw kolumn z bazy danych. Jest to pewna drobna niedogodność związana z używaniem kreatora do generowania encji JPA. Oczywiście można zmienić nazwę pola oraz jego getterów i setterów, a także zmodyfikować wartość atrybutu mappedBy w adnotacji @OneToMany po drugiej stronie relacji.
Zauważ, że dla wspomnianego pola ustawiono adnotację @ManyToOne. Ta adnotacja oznacza stronę „wiele” w relacji „jeden do wielu” między encjami Customer i Address. Zauważ, że dla pola ustawiono też adnotację @JoinColumn. Atrybut name w tej adnotacji oznacza, że dla odpo-
210
Rozdział 6. • Interakcja z bazami danych
wiadającej danej encji kolumny w bazie zdefiniowano ograniczenie klucza obcego. Dotyczy ono tabel ADDRESS i CUSTOMER. Atrybut referencedColumnName adnotacji @JoinColumn służy do wskazywania kolumny klucza głównego po stronie „jeden” relacji (tu jest to tabela CUSTOMER). Oprócz adnotacji dla relacji „jeden do wielu” i „wiele do jednego”, w encjach JPA używane są adnotacje dla relacji „wiele do wielu” i „jeden do jednego”. W używanym schemacie występuje relacja „wiele do wielu” między tabelami CUSTOMER_ORDER i ITEM, ponieważ jedno zamówienie może obejmować wiele produktów, a każdy produkt może występować w wielu zamówieniach. Tabela na zamówienia nosi nazwę CUSTOMER_ORDER, ponieważ samo słowo „order” jest w SQL-u słowem zarezerwowanym.
Przyjrzyj się encji JPA CustomerOrder, aby zobaczyć, jak definiowane są relacje „wiele do wielu”. package com.ensode.jpa; // Instrukcje importu pominięto @Entity @Table(name = "CUSTOMER_ORDER") @NamedQueries({ @NamedQuery(name = "CustomerOrder.findAll", query = "SELECT c FROM CustomerOrder c"), @NamedQuery(name = "CustomerOrder.findByCustomerOrderId", query = "SELECT c FROM CustomerOrder c WHERE " + "c.customerOrderId = :customerOrderId"), @NamedQuery(name = "CustomerOrder.findByOrderNumber", query = "SELECT c FROM CustomerOrder c WHERE " + "c.orderNumber = :orderNumber"), @NamedQuery(name = "CustomerOrder.findByOrderDescription", query = "SELECT c FROM CustomerOrder c WHERE " + "c.orderDescription = :orderDescription")}) public class CustomerOrder implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @Column(name = "CUSTOMER_ORDER_ID") private Integer customerOrderId; @Size(max = 10) @Column(name = "ORDER_NUMBER") private String orderNumber; @Size(max = 200) @Column(name = "ORDER_DESCRIPTION") private String orderDescription; @JoinTable(name = "ORDER_ITEM", joinColumns = { @JoinColumn(name = "CUSTOMER_ORDER_ID", referencedColumnName = "CUSTOMER_ORDER_ID")},
211
Java EE 6. Tworzenie aplikacji w NetBeans 7
inverseJoinColumns = { @JoinColumn(name = "ITEM_ID", referencedColumnName = "ITEM_ID")}) @ManyToMany private Collection itemCollection; @JoinColumn(name = "CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID") @ManyToOne private Customer customerId; // Konstruktory, gettery, settery oraz metody equals(), hashCode() i toString() // pominięto }
Zauważ, że encja CustomerOrder ma właściwość itemCollection typu java.util.Collection. Ta właściwość przechowuje wszystkie produkty z zamówienia. Warto zwrócić uwagę na to, że zastosowano tu adnotację @ManyToMany. Ta adnotacja pozwala tu zadeklarować relację „wiele do wielu” między encjami JPA CustomerOrder i Item. Ponadto zastosowano adnotację @JoinTable. Jest ona niezbędna, ponieważ jeśli w schemacie bazy danych istnieją relacje „wiele do wielu” między tabelami, potrzebna jest tabela łącznikowa. Zastosowanie takiej tabeli pomaga znormalizować dane w bazie. Adnotacja @JoinTable pozwala określić tabelę schematu odpowiadającą danej relacji „wiele do wielu”. Wartość atrybutu name w adnotacji @JoinTable musi pasować do nazwy tabeli łącznikowej ze schematu. Wartością atrybutu joinColumns adnotacji @JoinColumn musi być relacja oparta na kluczu obcym między tabelą łącznikową a nadrzędną tabelą z danymi. Omówienie tej adnotacji znajdziesz w opisie relacji „jeden do wielu”. Tu atrybut name musi mieć wartość odpowiadającą nazwie kolumny z tabeli łącznikowej z relacją klucza obcego, a wartością atrybutu referencedColumnName musi być nazwa kolumny klucza obcego nadrzędnej tabeli z danymi. Atrybut inverseJoinColumns adnotacji @JoinTable pełni podobną funkcję co atrybut joinColumns, ale określa kolumny z podrzędnej tabeli z danymi. Tabela z relacji „wiele do wielu” mająca wymienione powyżej adnotacje jest nazywana nadrzędną stroną relacji (ang. owning side). Przyjrzyj się teraz, jak relacja „wiele do wielu” jest zdefiniowana w podrzędnej stronie relacji (ang. non-owning side). Tu tą stroną jest encja JPA Item. package com.ensode.jpa; // Instrukcje importu pominięto @Entity @Table(name = "ITEM") @NamedQueries({ @NamedQuery(name = "Item.findAll", query = "SELECT i FROM Item i"), @NamedQuery(name = "Item.findByItemId", query = "SELECT i FROM Item i WHERE " + "i.itemId = :itemId"), @NamedQuery(name = "Item.findByItemNumber", query = "SELECT i FROM Item i WHERE " + "i.itemNumber = :itemNumber"),
212
Rozdział 6. • Interakcja z bazami danych
@NamedQuery(name = "Item.findByItemShortDesc", query = "SELECT i FROM Item i WHERE " + "i.itemShortDesc = :itemShortDesc"), @NamedQuery(name = "Item.findByItemLongDesc", query = "SELECT i FROM Item i WHERE " + "i.itemLongDesc = :itemLongDesc")}) public class Item implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @Column(name = "ITEM_ID") private Integer itemId; @Size(max = 10) @Column(name = "ITEM_NUMBER") private String itemNumber; @Size(max = 100) @Column(name = "ITEM_SHORT_DESC") private String itemShortDesc; @Size(max = 500) @Column(name = "ITEM_LONG_DESC") private String itemLongDesc; @ManyToMany(mappedBy = "itemCollection") private Collection customerOrderCollection; // Konstruktory, gettery, settery, metody equals() i hashCode() // zostały pominięte }
Jak widać, jedyną rzeczą, jaką trzeba zrobić po tej stronie relacji, jest utworzenie właściwości Collection, dodanie do niej adnotacji @ManyToMany i określenie w atrybucie mappedBy nazwy właściwości po drugiej stronie relacji. Oprócz relacji „jeden do wielu” i „wiele do wielu”, można dla encji JPA tworzyć relacje „jeden do jednego”. Adnotacja używana do tworzenia relacji „jeden do jednego” między encjami JPA to @OneToOne. W przykładowym schemacie nie ma relacji tego typu, dlatego kreator nie dodał tej adnotacji do żadnej z wygenerowanych encji. Relacje „jeden do jednego” występują w schematach baz danych stosunkowo rzadko. Zwykle wszystkie dane z jednej encji są przechowywane w jednej tabeli. Jednak interfejs JPA zapewnia obsługę relacji „jeden do jednego”, jeśli jest ona potrzebna.
Proces tworzenia relacji „jeden do jednego” między dwoma encjami jest podobny do wcześniej opisanej metody. Po nadrzędnej stronie relacji trzeba utworzyć pole typu encji JPA występującej po drugiej stronie relacji. Do tego pola trzeba dodać adnotacje @OneToOne i @JoinColumn.
213
Java EE 6. Tworzenie aplikacji w NetBeans 7
Załóżmy, że w schemacie występuje relacja „jeden do jednego” między tabelami PERSON (osoba) i BELLY_BUTTON (pępek). Każda osoba ma jeden pępek, a każdy pępek należy do tylko jednej osoby (nie potrafię wyjaśnić, dlaczego schemat utworzono w ten sposób, zamiast umieścić kolumny z tabeli BELLY_BUTTON w tabeli PERSON; po prostu nie udało mi się wymyślić lepszego przykładu). @Entity public class Person implements Serializable { @JoinColumn(name="BELLY_BUTTON_ID") @OneToOne private BellyButton bellyButton; public BellyButton getBellyButton(){ return bellyButton; } public void setBellyButton(BellyButton bellyButton){ this.bellyButton = bellyButton; } }
Jeśli dana relacja „jeden do jednego” jest jednokierunkowa (gdy można tylko ustalić pępek na podstawie osoby), nie trzeba robić nic więcej. Jeżeli relacja jest dwukierunkowa, należy dodać adnotację @OneToOne także po drugiej stronie relacji i użyć atrybutu mappedBy, aby wskazać drugą stronę. @Entity @Table(name="BELLY_BUTTON") public class BellyButton implements Serializable( { @OneToOne(mappedBy="bellyButton") private Person person; public Person getPerson(){ return person; } public void getPerson(Person person){ this.person=person; } }
Jak widać, proces tworzenia relacji „jeden do jednego” bardzo przypomina tworzenie relacji „jeden do wielu” i „wiele do wielu”. Po wygenerowaniu encji JPA na podstawie bazy danych należy napisać dodatkowy kod warstwy biznesowej i warstwy prezentacji. Można też wygenerować kod obu tych warstw za pomocą środowiska NetBeans.
214
Rozdział 6. • Interakcja z bazami danych
Generowanie aplikacji JSP na podstawie encji JPA Atrakcyjną cechą środowiska NetBeans jest to, że umożliwia generowanie aplikacji JSF wykonującej operacje CRUD (ang. Create, Read, Update, and Delete, czyli tworzenie, wczytywanie, aktualizowanie i usuwanie) na istniejących encjach JPA. Mechanizm ten w połączeniu z możliwością tworzenia encji JPA na podstawie schematu istniejącej bazy danych (opisano to w poprzednim podrozdziale) pozwala błyskawicznie pisać aplikacje sieciowe komunikujące się z bazami danych. Aby wygenerować strony JSP na podstawie istniejących encji JPA, należy kliknąć projekt prawym przyciskiem myszy, wybrać opcję File/New File, a następnie otworzyć kategorię JavaServer Faces i kliknąć typ pliku JSF Pages from Entity Classes (rysunek 6.21).
Rysunek 6.21. Generowanie stron JSF na podstawie encji Aby możliwe było generowanie stron JSF na podstawie istniejących encji JPA, projektem musi być aplikacja sieciowa.
Po kliknięciu przycisku Next> należy wybrać jedną lub więcej encji JPA. Zwykle potrzebne są wszystkie encje. Można je łatwo zaznaczyć za pomocą przycisku Add All>> (rysunek 6.22).
215
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 6.22. Wybieranie encji dla aplikacji
Na następnej stronie kreatora można określić pakiet tworzonego ziarna zarządzanego JSF (rysunek 6.23). Kreator tworzy dwa rodzaje klas: kontrolery JPA i klasy JSF. Można określić pakiety osobno dla tych dwóch rodzajów klas. Można też podać katalog na tworzone strony JSF. Jeśli go nie wskażesz, strony zostaną umieszczone w katalogu Web Pages projektu. Wartość pól tekstowych Session Bean Package i JSF Classes Package to domyślnie nazwa pakietu, w którym znajdują się używane encje JPA. Warto zmodyfikować to domyślne ustawienie, ponieważ umieszczenie ziaren zarządzanych JSF w innym pakiecie pozwala oddzielić klasy warstwy dostępu do danych od warstw interfejsu użytkownika i kontrolera. Gdy klikniesz przycisk Finish, utworzona zostanie kompletna aplikacja sieciowa wykonująca operacje CRUD (rysunek 6.24). Jak widać, środowisko NetBeans generuje w katalogu Web Pages aplikacji podkatalog dla każdej encji. Każdy z tych podkatalogów zawiera pliki XHTML Detail, Edit, List i New. Pliki te to strony JSF wyświetlane za pomocą faceletów. Strona Detail wyświetla wszystkie właściwości encji JPA, strona Edit umożliwia modyfikowanie informacji z danej encji, strona List wyświetla wszystkie wystąpienia encji w bazie danych, a na stronie New można tworzyć nowe encje.
216
Rozdział 6. • Interakcja z bazami danych
Rysunek 6.23. Określanie pakietów dla generowanych klas
Rysunek 6.24. Wygenerowana kompletna aplikacja sieciowa
Generowana jest standardowa aplikacja JSF. Aby ją uruchomić, wystarczy kliknąć projekt prawym przyciskiem myszy i wybrać opcję Run. Wtedy wykonywane są standardowe operacje: uruchamiany jest serwer aplikacji (jeśli wcześniej go nie włączono) i otwiera się okno przeglądarki ze stroną startową aplikacji (rysunek 6.25).
217
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 6.25. Strona startowa aplikacji
Jak widać, na stronie startowej znajdują się odnośniki odpowiadające encjom JPA. Wyświetlają one tabele z wszystkimi wystąpieniami poszczególnych encji w bazie danych. Gdy klikniesz odnośnik Show All Customer Items, pojawi się strona z rysunku 6.26.
Rysunek 6.26. Wszystkie encje typu Customer
Ponieważ na razie w bazie nie zapisano żadnych danych, strona wyświetla komunikat (No Customer Items Found). Możesz wstawić dane klienta do bazy, klikając odnośnik Create New Customer (rysunek 6.27). Zauważ, że dla każdej właściwości encji (odpowiadają one kolumnom z tabeli bazy danych) generowane jest pole na dane wejściowe. Wygenerowane zostało tu pole na dane wejściowe dla klucza głównego encji. Pole to jest generowane tylko wtedy, gdy dla encji JPA nie wybrano strategii generowania kluczy głównych.
218
Rozdział 6. • Interakcja z bazami danych
Rysunek 6.27. Tworzenie nowego egzemplarza encji
Gdy wprowadzisz dane na stronie i klikniesz odnośnik Save, aplikacja zapisze dane, wyczyści formularz i wyświetli komunikat Customer was successfully created (rysunek 6.28).
Rysunek 6.28. Komunikat wyświetlany po udanym utworzeniu nowego egzemplarza encji
Za pomocą odnośnika Show All Customer Items można wyświetlić dane nowo utworzonego klienta (rysunek 6.29).
219
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 6.29. Dane nowego klienta
Na tym etapie można wyświetlić dane nowo utworzonego klienta na liście klientów na stronie JSP. Zauważ, że na stronie znajdują się odnośniki View, Edit i Destroy. Pozwalają one wyświetlić, zmodyfikować i usunąć encję. Załóżmy, że użytkownik chce dodać adres klienta. W tym celu należy kliknąć odnośnik Index, a następnie odnośniki Show All Address Items i New Address (rysunek 6.30).
Rysunek 6.30. Dodawanie nowego adresu
220
Rozdział 6. • Interakcja z bazami danych
Encja Address to strona „jeden” kilku relacji „jeden do wielu”. Zauważ, że dla każdej encji po stronie „wiele” generowane jest pole kombi. Ponieważ adres ma zostać przypisany do dodanego wcześniej klienta, spróbuj wybrać klienta w polu kombi CustomerId. Dla pola CustomerId można wymyślić lepszą nazwę. Ta została ustawiona, ponieważ odpowiada nazwie właściwości encji JPA Address. Właściwość ta mogłaby mieć lepszą nazwę, na przykład customer. Warto przypomnieć, że wszystkie encje w projekcie zostały automatycznie wygenerowane na podstawie schematu istniejącej bazy danych.
Gdy klikniesz wspomniane pole kombi, zobaczysz tajemnicze, niemal niemożliwe do zrozumienia (przynajmniej dla użytkowników) określenie klienta. Wynika to z tego, że etykiety generowane dla pozycji w polu kombi są tworzone za pomocą metody toString() odpowiednich encji. Aby rozwiązać ten problem, można zmodyfikować metodę toString() tak, żeby zwracała zrozumiały dla użytkowników obiekt typu String, który można wykorzystać jako etykietę. Jak widać, kod wygenerowany przez kreatory ze środowiska NetBeans wymaga wprowadzenia pewnych poprawek: zmodyfikowania metod toString() w każdej encji JPA, aby zwracany tekst można było wyświetlić jako etykietę, zmiany nazw właściwości w encjach, żeby były bardziej sensowne dla programistów, lub zmodyfikowania etykiet wygenerowanych stron JSF, by były bardziej przyjazne dla użytkowników. Ponadto same strony nie są atrakcyjne wizualnie. Warto je zmodyfikować, aby nie wyglądały tak nudno. Jednak za pomocą kilku kliknięć myszą udało się utworzyć działającą aplikację. To rozwiązanie pozwala znacznie przyspieszyć i ułatwić pracę (tylko nie wspominaj o tym przełożonemu!).
Podsumowanie W tym rozdziale poznałeś wiele technik środowiska NetBeans, które pozwalają przyspieszyć tworzenie aplikacji dzięki wykorzystaniu interfejsu JPA. Zobaczyłeś, w jaki sposób środowisko NetBeans może generować nowe klasy JPA z wszystkimi potrzebnymi adnotacjami. Dowiedziałeś się, że środowisko NetBeans potrafi automatycznie generować kod odpowiedzialny za utrwalanie encji JPA w tabelach bazy danych. Dowiedziałeś się też, że środowisko NetBeans potrafi generować encje JPA na podstawie schematu istniejącej bazy danych (proces ten obejmuje automatyczne generowanie kwerend nazwanych w języku JPQL i obsługę sprawdzania poprawności danych). Teraz już wiesz, że za pomocą środowiska NetBeans można wygenerować kompletną aplikację JSF na podstawie istniejących encji JPA.
221
Java EE 6. Tworzenie aplikacji w NetBeans 7
222
7 Implementowanie warstwy biznesowej za pomocą ziaren sesyjnych W większości rozbudowanych aplikacji dla firm trzeba spełnić pewne typowe wymagania związane z transakcjami, bezpieczeństwem, skalowalnością itd. Ziarna EJB (ang. Enterprise JavaBeans) umożliwiają programistom skoncentrowanie się na logice biznesowej bez konieczności zajmowania się spełnianiem wspomnianych wymagań. Istnieją dwa rodzaje ziaren EJB: sesyjne i sterowane komunikatami. W tym rozdziale znajdziesz omówienie ziaren sesyjnych. Ziarna EJB tego typu znacznie upraszczają tworzenie logiki biznesowej działającej po stronie serwera. W następnym rozdziale opisane są ziarna sterowane komunikatami. Umożliwiają one łatwe implementowanie w aplikacjach mechanizmów przekazywania komunikatów. We wcześniejszych wersjach J2EE używane były też ziarna encyjne. Od Javy EE 5 ziarna encyjne są uznawane za przestarzałe (zastąpił je interfejs JPA).
W tym rozdziale omówiono następujące zagadnienia: wprowadzenie do ziaren sesyjnych; tworzenie ziaren sesyjnych w środowisku NetBeans; zarządzanie transakcjami za pomocą ziaren EJB; programowanie aspektowe z wykorzystaniem interceptorów;
Java EE 6. Tworzenie aplikacji w NetBeans 7
usługi zegara w ziarnach EJB; generowanie ziaren sesyjnych na podstawie encji JPA.
Wprowadzenie do ziaren sesyjnych Ziarna sesyjne zawierają logikę biznesową w dużych aplikacjach dla firm. Przy tworzeniu takich aplikacji warto korzystać z ziaren sesyjnych, ponieważ dzięki temu programiści mogą skoncentrować się na pracy nad logiką biznesową i nie muszą zajmować się innymi wymogami dotyczącymi takich aplikacji (skalowalnością, bezpieczeństwem, transakcjami itd.). Choć programiści aplikacji nie implementują bezpośrednio wymagań typowych dla dużych aplikacji dla firm (związanych np. z transakcjami i bezpieczeństwem), można skonfigurować potrzebne usługi za pomocą adnotacji.
Istnieją dwa rodzaje ziaren sesyjnych: bezstanowe i stanowe. Różnica między nimi polega na tym, że w stanowych ziarnach sesyjnych między wywołaniami metod zachowywany jest stan komunikacji z klientem. W ziarnach bezstanowych stan nie jest zapisywany.
Tworzenie ziaren sesyjnych w środowisku NetBeans Ziarna sesyjne można tworzyć w trzech typach projektów środowiska NetBeans: Enterprise Application, EJB Module i Web Application. Projekty EJB Module mogą zawierać wyłącznie ziarna EJB, natomiast w projektach Enterprise Application można umieścić ziarna EJB razem z ich klientami. Klientem może tu być aplikacja sieciowa lub niezależna aplikacja w Javie. Możliwość dodawania ziaren EJB do aplikacji sieciowych to nowa funkcja dodana w Javie EE 6. Rozwiązanie to pozwala uprościć tworzenie pakietów i instalowanie aplikacji sieciowych używających ziaren EJB. Obecnie można utworzyć pakiet z kodem aplikacji sieciowej i kodem ziarna EJB oraz zapisać go w jednym pliku WAR. We wcześniejszych wersjach Javy EE i J2EE trzeba było utworzyć plik EAR (ang. Enterprise Application). Przy instalowaniu dużych aplikacji dla firm na serwerze aplikacji GlassFish dostępnym razem ze środowiskiem NetBeans można zainstalować niezależne klienty jako część aplikacji. Klienty te można następnie uruchamiać za pomocą technologii Java Web Start (http://java.sun. com/products/javawebstart/). Można też łatwo uzyskać dostęp do ziaren EJB w kodzie klienta, wykorzystując adnotacje. W naprawdę samodzielnych klientach, działających poza serwerem aplikacji, uzyskanie referencji do ziaren EJB wymaga wyszukiwania identyfikatorów JNDI. Aby utworzyć projekt Enterprise Application, wybierz opcję File/New Project, otwórz kategorię Enterprise, a następnie kliknij pozycję Enterprise Application (rysunek 7.1).
224
Rozdział 7. • Implementowanie warstwy biznesowej
Rysunek 7.1. Tworzenie projektu typu Enterprise Application
Po kliknięciu przycisku Next> należy wprowadzić nazwę projektu (rysunek 7.2).
Rysunek 7.2. Podawanie nazwy projektu
225
Java EE 6. Tworzenie aplikacji w NetBeans 7
Zwykle wartości domyślne w polach Project Location i Project Folder są odpowiednie, dlatego nie trzeba ich zmieniać. Na następnej stronie trzeba wybrać moduły dołączane tworzonej aplikacji (rysunek 7.3). Domyślnie zaznaczone są pola Create EJB Module i Create Web Application Module. W tym przykładzie potrzebny jest moduł klienta, natomiast nie trzeba tworzyć modułu aplikacji sieciowej, dlatego zmień zaznaczenie w odpowiednich polach.
Rysunek 7.3. Zaznacz pole Create Application Client Module i usuń zaznaczenie w polu Create Web Application Module1
Gdy tworzony jest moduł klienta, pakiet z główną klasą domyślnie przyjmuje nazwę projektu (pisaną małymi literami). Nazwa ta nie jest zgodna ze standardowymi konwencjami nazewnictwa pakietów Javy, które domyślnie zaczynają się od podawanej od tyłu nazwy domeny (com. nazwafirmy dla domeny nazwafirmy.com). Dlatego warto zmienić domyślną nazwę na wartość zgodną z konwencją. Gdy klikniesz przycisk Finish, w oknie Project powinny pojawić się trzy nowe projekty (rysunek 7.4).
1
Opcja Create Application Client Module nie jest dostępna w wersji 7.0 środowiska NetBeans; przedstawione tu zrzuty wykonano w wersji 6.9 — przyp. tłum.
226
Rozdział 7. • Implementowanie warstwy biznesowej
Rysunek 7.4. Nowe projekty w oknie Projects
W tym przykładzie SessionBeanIntro to projekt typu Enterprise Application, SessionBeanIntro-app-client to moduł klienta aplikacji, a SessionBeanIntro-ejb to moduł ziarna EJB. Później potrzebny będzie też projekt typu Java Class Library (więcej na ten temat dowiesz się w dalszej części tekstu), dlatego można od razu go utworzyć (rysunek 7.5).
Rysunek 7.5. Tworzenie nowego projektu typu Java Class Library
Na następnej stronie kreatora wystarczy podać nazwę projektu i kliknąć przycisk Finish (rysunek 7.6).
227
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 7.6. Podaj nazwę projektu i kliknij przycisk Finish
Po przygotowaniu projektów pora utworzyć pierwsze ziarno sesyjne. Aby to zrobić, kliknij moduł EJB, wybierz opcję File/New File, otwórz kategorię Java EE, a następnie kliknij typ pliku Session Bean w kreatorze New File (rysunek 7.7).
Rysunek 7.7. Tworzenie ziarna sesyjnego
228
Rozdział 7. • Implementowanie warstwy biznesowej
Trzeba ustawić tu kilka opcji: Warto zmienić domyślną nazwę ustawioną dla ziarna sesyjnego. Trzeba określić pakiet dla ziarna sesyjnego. Należy określić typ ziarna sesyjnego (bezstanowe, stanowe lub singleton):
Stanowe ziarno sesyjne zachowuje stan komunikacji z klientem. Oznacza to, że wartości zmiennych składowych pozostają stałe między wywołaniami metod. Bezstanowe ziarno sesyjne nie zachowuje stanu komunikacji, dlatego ma wyższą wydajność niż ziarno stanowe. Ziarna sesyjne typu singleton to nowy rodzaj ziaren sesyjnych wprowadzony w Javie EE 6. Działa on tak, że w momencie instalowania aplikacji tworzony jest jeden egzemplarz ziarna sesyjnego tego typu. Ziarna sesyjne typu singleton są przydatne do zapisywania w pamięci podręcznej często wczytywanych danych z bazy.
Trzeba określić, czy ziarno sesyjne ma mieć interfejs zdalny (używany dla klientów
działających w innej maszynie JVM niż dane ziarno), interfejs lokalny (przeznaczony dla klientów uruchamianych w tej samej maszynie JVM co dane ziarno), czy też oba interfejsy. Nowością w Javie EE 6 jest to, że interfejsy lokalne są opcjonalne. Dlatego nie trzeba tworzyć żadnego interfejsu dla ziaren sesyjnych, które będą używane tylko przez klienty uruchamiane w tej samej maszynie wirtualnej. Rysunek 7.8 przedstawia konfigurowanie nowego ziarna.
Rysunek 7.8. Konfigurowanie nowego ziarna sesyjnego
229
Java EE 6. Tworzenie aplikacji w NetBeans 7
Przykładowe ziarno nie musi zachowywać stanu komunikacji z klientami, dlatego można utworzyć je jako bezstanowe ziarno sesyjne. Jedyny klient tego ziarna będzie działał w innej maszynie JVM, dlatego trzeba utworzyć interfejs zdalny. Interfejs lokalny nie będzie potrzebny. Przy tworzeniu interfejsów zdalnych środowisko NetBeans wymaga wskazania biblioteki klienta, w której znajdzie się taki interfejs. To dlatego wcześniej utworzona została biblioteka klas Javy. Domyślnie wybierana jest biblioteka klienta. Gdy zaznaczysz odpowiednie opcje i klikniesz przycisk Finish, ziarno sesyjne zostanie utworzone w projekcie modułu EJB, a interfejs zdalny zostanie wygenerowany w projekcie biblioteki klienta (rysunek 7.9).
Rysunek 7.9. Nowe elementy są dodawane do odpowiednich projektów
Wygenerowany kod ziarna sesyjnego to pusta klasa z dodaną adnotacją @Stateless (rysunek 7.10). Zauważ, że ziarno zawiera implementację interfejsu zdalnego. Na tym etapie interfejs ten jest pusty i ma adnotację @Remote (rysunek 7.11). Adnotacja ta została dodana, ponieważ interfejs ma być zdalny. Interfejs zdalny i (lub) opcjonalny interfejs lokalny jest potrzebny, ponieważ klient ziarna sesyjnego nie może wywoływać metod ziarna bezpośrednio. Musi pobrać referencję do klasy z implementacją odpowiedniego interfejsu i wywoływać metody z poziomu tej klasy. Od Javy EE 6 tworzenie interfejsu lokalnego nie jest konieczne. Serwer aplikacji potrafi automatycznie wygenerować taki interfejs w momencie instalowania aplikacji.
230
Rozdział 7. • Implementowanie warstwy biznesowej
Rysunek 7.10. Wygenerowane ziarno sesyjne
Rysunek 7.11. Utworzony interfejs jest na razie pusty
Implementacja interfejsu zdalnego i (lub) lokalnego jest tworzona automatycznie przez kontener ziarna EJB w momencie instalowania danego ziarna. Implementacja ta odpowiada za operacje wykonywane przed wywołaniem metod ziarna sesyjnego. Ponieważ metody te trzeba zdefiniować zarówno w interfejsie, jak i w ziarnie, zwykle sygnaturę metody należy umieścić i w ziarnie, i w odpowiednich interfejsach. Jednak przy używaniu ziaren sesyjnych w środowisku NetBeans wystarczy kliknąć kod źródłowy ziarna prawym przyciskiem myszy i wybrać opcję Insert Code/Add Business Method (rysunek 7.12). Powoduje to dodanie metody do ziarna i odpowiedniego interfejsu.
231
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 7.12. Dodawanie metody do ziarna i interfejsu
Gdy wybierzesz tę opcję, pojawi się okno dialogowe, w którym należy podać nazwę metody, typ zwracanej wartości, parametry i interfejsy (zdalny i (lub) lokalny), do których należy dodać daną metodę (rysunek 7.13).
Rysunek 7.13. Konfigurowanie dodawanej metody
Tu dodawana jest metoda echo, która przyjmuje wartość typu String jako parametr i zwraca wartość tego samego typu. Ponieważ tworzone ziarno ma tylko interfejs zdalny, przyciski opcji Local i Both są nieaktywne. Po wprowadzeniu odpowiednich informacji metoda jest dodawana do ziarna i interfejsu zdalnego (rysunek 7.14). Implementacja domyślna zwraca wartość null. W tym prostym przykładzie zmodyfikuj metodę tak, aby zwracała tekst "Przekazany parametr: " połączony z przekazanym parametrem (rysunek 7.15).
232
Rozdział 7. • Implementowanie warstwy biznesowej
Rysunek 7.14. Nowa metoda w kodzie ziarna
Rysunek 7.15. Zmodyfikowana wersja metody ziarna
Na tym etapie gotowe jest proste, ale kompletne bezstanowe ziarno sesyjne.
Dostęp do ziarna z poziomu klienta Pora skoncentrować się na kliencie. W projekcie zdalnego klienta trzeba wykorzystać projekt typu Java Class Library z interfejsem zdalnym. Dodawanie projektu wspomnianego typu jest bardzo proste. Wystarczy kliknąć prawym przyciskiem myszy węzeł Libraries w projekcie i wybrać opcję Add Project… (rysunek 7.16). Następnie należy wybrać projekt dodawany jako biblioteka (rysunek 7.17). Projekt biblioteki klas Javy zostaje dodany do węzła Libraries klienta ziarna EJB (rysunek 7.18). Teraz można wywołać metodę ziarna EJB. W kodzie klienta trzeba pobrać referencję do obiektu klasy z implementacją interfejsu zdalnego. W środowisku NetBeans jest to bardzo łatwe. Wystarczy kliknąć prawym przyciskiem myszy kod klienta (com.ensode.sessionbeanintro. Main w projekcie klienta w przykładowej aplikacji) i wybrać opcję Insert Code…/Call Enterprise Bean (rysunek 7.19).
233
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 7.16. Dodawanie projektu biblioteki
Rysunek 7.17. Wybieranie dodawanej biblioteki
234
Rozdział 7. • Implementowanie warstwy biznesowej
Rysunek 7.18. Biblioteka jest dodawana do projektu klienta
Rysunek 7.19. Pobieranie referencji do obiektu z implementacją interfejsu zdalnego
Teraz można wywołać metodę ziarna EJB. W kodzie klienta trzeba pobrać referencję do obiektu klasy z implementacją interfejsu zdalnego. W środowisku NetBeans jest to bardzo łatwe. Wystarczy kliknąć prawym przyciskiem myszy kod klienta (com.ensode.sessionbeanintro. Main w projekcie klienta w przykładowej aplikacji) i wybrać opcję Insert Code…/Call Enterprise Bean (rysunek 7.19). Na tym etapie pojawia się lista wszystkich otwartych projektów z ziarnami sesyjnymi (rysunek 7.20). Należy wybrać z jednego z tych projektów potrzebne ziarno. Jeśli dla danego ziarna istnieją interfejsy lokalny i zdalny, można też wybrać odpowiedni interfejs. Jednak ponieważ tu dostępny jest tylko interfejs zdalny, opcja dla interfejsu lokalnego jest nieaktywna. Tu nawet gdyby można było wybrać interfejs lokalny, należałoby wybrać interfejs zdalny, ponieważ klient działa w innej maszynie JVM niż serwer, a z innej maszyny JVM nie można uzyskać dostępu do interfejsu lokalnego.
235
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 7.20. Wybieranie wywoływanego ziarna sesyjnego
Na tym etapie do klienta dodawana jest zmienna składowa EchoRemote (reprezentuje ona interfejs zdalny ziarna) z adnotacją @EJB (rysunek 7.21). Adnotacja ta powoduje dołączenie w czasie wykonywania programu egzemplarza interfejsu zdalnego.
Rysunek 7.21. Dodana referencja do interfejsu zdalnego
236
Rozdział 7. • Implementowanie warstwy biznesowej
We wcześniejszych wersjach J2EE uzyskanie referencji do macierzystego interfejsu ziarna wymagało wyszukiwania identyfikatorów JNDI. Następnie za pomocą interfejsu macierzystego można było uzyskać dostęp do referencji do interfejsu zdalnego lub lokalnego. Jak widać, w Javie EE obecnie znacznie uproszczono proces pobierania referencji do ziaren EJB.
Teraz wystarczy dodać wywołanie metody echo() interfejsu zdalnego i klient będzie gotowy (rysunek 7.22).
Rysunek 7.22. Dodanie wywołania metody echo() kończy proces tworzenia klienta
Uruchamianie klienta Aby uruchomić klienta, kliknij prawym przyciskiem myszy projekt typu Enterprise Application i wybierz opcję Run. Po kilku sekundach powinieneś zobaczyć okno dialogowe z rysunku 7.23 z tekstem zwróconym przez metodę ziarna sesyjnego.
Rysunek 7.23. Okno z komunikatem z ziarna sesyjnego
Klienty instalowane w ten sposób wykorzystują technologię Java Web Start. Aplikacje używające tej technologii są uruchamiane na klienckiej stacji roboczej, jednak można je wykonywać także za pomocą adresów URL. Startowy adres URL dla modułów klienckich aplikacji typu Enterprise Application domyślnie odpowiada nazwie projektu tej aplikacji, po której następuje nazwa modułu klienta. W omawianym przykładzie adres ten to http://localhost:8080/ SessionBeanIntro/SessionBeanIntro-app-client. Można się o tym przekonać, wpisując go w polu adresu przeglądarki. Po krótkiej chwili uruchomiony zostanie klient aplikacji.
237
Java EE 6. Tworzenie aplikacji w NetBeans 7
Zarządzanie transakcjami w ziarnach sesyjnych Jak wcześniej wspomniano, jedną z zalet ziaren EJB jest to, że automatycznie obsługują transakcje. Jednak aby uzyskać większą kontrolę nad zarządzaniem transakcjami, trzeba skonfigurować ich obsługę. Transakcje umożliwiają wykonanie wszystkich kroków metody lub — jeśli jeden z etapów zakończy się niepowodzeniem (np. w wyniku zgłoszenia wyjątku) — anulowanie zmian wprowadzonych w metodzie. Przede wszystkim trzeba skonfigurować działanie ziarna w sytuacji, gdy jedna z metod zostanie wywołana w trakcie wykonywania transakcji. Czy metoda ziarna powinna wtedy stać się częścią istniejącej transakcji? A może należy wstrzymać bieżącą transakcję i utworzyć nową dla wywołanej metody ziarna? Można to skonfigurować za pomocą adnotacji @TransactionAttribute. Adnotacja @TransactionAttribute umożliwia kontrolowanie działania metod EJB, gdy zostaną wywołane w trakcie wykonywania transakcji i gdy żadna transakcja akurat nie jest obsługiwana. Adnotacja ta ma jeden atrybut, value, który służy do określania, jak transakcja ma działać w obu tych sytuacjach. W tabeli 7.1 znajdziesz listę różnych wartości, jakie można przypisać do adnotacji @Transaction Attribute. Tabela 7.1. Dozwolone wartości adnotacji @TransactionAttribute W momencie wywołania metody żadna transakcja nie była wykonywana
Wartość adnotacji @TransactionAttribute
Metodę wywołano w trakcie wykonywania transakcji
TransactionAttributeType. MANDATORY
Metoda staje się częścią wykonywanej Zgłaszany jest wyjątek transakcji. TransactionRequiredException. Zgłaszany jest wyjątek RemoteException. Metoda jest wykonywana bez obsługi transakcji. Transakcja klienta jest wstrzymywana, Metoda jest wykonywana metoda zostaje wykonana bez obsługi bez obsługi transakcji. transakcji, po czym transakcja klienta jest wznawiana. Metoda staje się częścią istniejącej Dla metody tworzona jest nowa transakcji. transakcja. Transakcja klienta jest wstrzymywana, Dla metody tworzona jest nowa utworzona zostaje nowa transakcja dla transakcja. metody, po czym transakcja klienta jest wznawiana. Metoda staje się częścią istniejącej Metoda jest wykonywana transakcji. bez obsługi transakcji.
TransactionAttributeType. NEVER TransactionAttributeType. NOT_SUPPORTED
TransactionAttributeType. REQUIRED TransactionAttributeType. REQUIRES_NEW
TransactionAttributeType. SUPPORTS
238
Rozdział 7. • Implementowanie warstwy biznesowej
Adnotację @TransactionAttribute można dodać do deklaracji klasy ziarna EJB lub do pojedynczej metody. Jeśli adnotacja znajduje się przy deklaracji klasy, określone działanie transakcji dotyczy wszystkich metod ziarna. Jeżeli adnotacja występuje przy jednej metodzie, dotyczy tylko jej. Gdy w ziarnie adnotacje @TransactionAttribute występują zarówno na poziomie klasy, jak i na poziomie metody, uwzględniana jest adnotacja dla metody. Jeśli programista nie podał omawianej adnotacji, domyślnie używane jest ustawienie TransactionAttributeType. REQUIRED. W poniższym przykładowym kodzie pokazano, jak używać tej adnotacji: package com.ensode.sessionbeanintro.ejb; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; @Stateless public class EchoBean implements EchoRemote { @TransactionAttribute( TransactionAttributeType.REQUIRES_NEW) public String echo(String saying) { return "Otrzymany parametr: " + saying; } }
Jak widać, aby skonfigurować transakcje dla jednej metody, wystarczy dodać do metody adnotację @TransactionAttribute z parametrem w postaci odpowiedniej stałej wyliczeniowej TransactionAttributeType. Jeśli chcesz zastosować tę samą strategię obsługi transakcji dla wszystkich metod, możesz umieścić adnotację @TransactionAttribute na poziomie klasy.
Programowanie aspektowe z wykorzystaniem interceptorów Czasem programista chce wykonywać określony kod przed głównym kodem metody i (lub) po nim. Możliwe, że chce mierzyć czas wykonywania metody, aby wykryć problemy z wydajnością, lub zamierza przesyłać komunikaty do dziennika przy każdym wejściu do metody i wyjściu z niej, co ułatwia śledzenie błędów i wyjątków. Najczęściej stosowanym rozwiązaniem tego rodzaju problemów jest dodanie fragmentu kodu na początku i końcu każdej metody. Kod ten powinien odpowiadać za profilowanie metody lub rejestrowanie danych w dzienniku. To podejście prowadzi jednak do pewnego problemu — kod trzeba napisać w kilku miejscach. Jeśli później zechcesz go zmodyfikować lub usunąć, będziesz musiał wprowadzić zmiany w kilku metodach. Programowanie aspektowe to paradygmat pozbawiony tej wady. Umożliwia zaimplementowanie w odrębnej klasie kodu wykonywanego przed głównym kodem metody lub po nim. W technologii EJB 3.0 do obsługi programowania aspektowego służą interceptory. 239
Java EE 6. Tworzenie aplikacji w NetBeans 7
Programowanie aspektowe z wykorzystaniem interceptorów obejmuje dwa etapy: pisanie klasy interceptora i dodawanie adnotacji @Interceptors do przechwytywanych ziaren EJB.
Tworzenie klasy interceptora Interceptor to standardowa klasa Javy, przy czym musi się w niej znajdować jedna metoda o następującej sygnaturze: @AroundInvoke public Object methodName(InvocationContext invocationContext) throws Exception
Zauważ, że do tej metody trzeba dodać adnotację @AroundInvoke, która określa, że dana metoda jest metodą interceptora. Za pomocą parametru InvocationContext można pobierać informacje na temat przechwytywanej metody (np. nazwę, parametry, klasę, w której dana metoda się znajduje itd.). Dla parametru dostępna jest też metoda proceed(), która pozwala określić, kiedy należy wykonać główny kod metody. W tabeli 7.2 znajdziesz opis najbardziej przydatnych metod z parametru InvocationContext. Więcej informacji znajdziesz w dokumentacji JavaDoc Javy EE 6 (w środowisku NetBeans możesz ją wyświetlić za pomocą opcji Help/JavaDoc References/Java EE 6 - DRAFT). Tabela 7.2. Najczęściej używane metody z parametru InvocationContext Nazwa metody
Opis
getMethod()
Zwraca obiekt typu java.lang.reflect.Method, który pozwala zbadać przechwytywaną metodę.
getParameters()
Zwraca tablicę obiektów typu Object zawierających parametry przekazywane do przechwytywanej metody.
getTarget()
Zwraca obiekt typu java.lang.Object zawierający wywoływaną metodę.
proceed()
Wywołuje przechwytywaną metodę.
Poniżej przedstawiono przykładową prostą klasę interceptora: package com.ensode.sessionbeanintro.ejb; import java.lang.reflect.Method; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; public class LoggingInterceptor { @AroundInvoke public Object logMethodCall( InvocationContext invocationContext) throws Exception { Object interceptedObject = invocationContext.getTarget();
240
Rozdział 7. • Implementowanie warstwy biznesowej
Method interceptedMethod = invocationContext.getMethod(); System.out.println("Start metody " + interceptedObject.getClass().getName() + "." + interceptedMethod.getName() + "()"); Object o = invocationContext.proceed(); System.out.println("Koniec metody " + interceptedObject.getClass().getName() + "." + interceptedMethod.getName() + "()"); return o; } }
Ten przykładowy kod wysyła komunikat do dziennika serwera aplikacji przed wykonaniem przechwytywanej metody i bezpośrednio po zakończeniu przez nią pracy. Takie rozwiązanie może pomóc w debugowaniu aplikacji. W przykładzie dla uproszczenia wykorzystano wywołanie System.out.println do przesłania komunikatów do dziennika serwera aplikacji. W rzeczywistych programach zwykle używa się interfejsów API do rejestrowania komunikatów (np. Java Logging API lub Log4j).
Pierwszą operacją wykonywaną w metodzie interceptora jest pobieranie referencji do przechwytywanego obiektu i jego metody. Następnie kod przesyła do dziennika komunikat z nazwą klasy i wywoływanej metody. Zadanie to jest wykonywane bezpośrednio przed wywołaniem przechwytywanej metody za pomocą instrukcji invocationContext.proceed(). Wartość zwrócona przez tę metodę jest zapisywana w zmiennej. Dalej znajduje się dodatkowy kod wykonywany bezpośrednio po zakończeniu pracy przez przechwytywaną metodę. W przykładzie kod ten przesyła dodatkowy wiersz tekstu do dziennika serwera aplikacji. W ostatnim kroku metoda zwraca wartość zwróconą przez wywołanie invocationContext.proceed().
Dodawanie adnotacji @Interceptors do ziaren EJB Aby móc przechwytywać metody ziarna EJB, trzeba dodać adnotację @Interceptions. Adnotacja ta ma jeden atrybut w postaci tablicy klas. Atrybut ten obejmuje wszystkie interceptory uruchamiane przed wywołaniem danej metody i (lub) po jej wykonaniu. Adnotację @Interceptors można zastosować na poziomie metody (wtedy dotyczy tylko danej metody) lub na poziomie klasy (wtedy obowiązuje dla wszystkich metod ziarna). Poniżej przedstawiono nową wersję ziarna sesyjnego EchoBean. Jest ona nieco zmodyfikowana. Metoda echo() jest teraz przechwytywana przez napisany w poprzednim punkcie interceptor LoggingInterceptor. package com.ensode.sessionbeanintro.ejb; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute;
241
Java EE 6. Tworzenie aplikacji w NetBeans 7
import javax.ejb.TransactionAttributeType; import javax.interceptor.Interceptors; @Stateless public class Echo implements EchoRemote { // Tu wstaw logikę biznesową. Kliknij prawym przyciskiem myszy w edytorze i // wybierz opcję Insert Code/Add Business Method @Interceptors({LoggingInterceptor.class}) @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public String echo(String saying) { return "Przekazany parametr: " + saying; } }
Zauważ, że jedyną niezbędną zmianą w tym ziarnie sesyjnym było dodanie adnotacji @Interceptors do metody echo(). Tu atrybut w postaci tablicy klas zawiera jedną wartość — zdefiniowaną wcześniej klasę LoggingInterceptors. To sprawia, że cały kod z metody logMethodCall() interceptora umieszczony przed wywołaniem invocationContext.proceed() jest uruchamiany bezpośrednio przed wywołaniem metody echo(). Ponadto cały kod po wywołaniu invocationContext.proceed() jest uruchamiany bezpośrednio po zakończeniu działania metody echo(). Tu dla metody ziarna używany jest jeden interceptor. Jeśli metoda ma być przechwytywana przez więcej niż jeden interceptor, można umieścić dodatkowe klasy interceptorów w nawiasach klamrowych w adnotacji @Interceptors. Interceptory na liście w nawiasach klamrowych muszą być rozdzielone przecinkami. Na tym etapie można przetestować interceptor. W środowisku NetBeans kliknij prawym przyciskiem myszy projekt w oknie Projects i wybierz opcję Run. W oknie z danymi wyjściowymi serwera GlassFish w środowisku NetBeans powinny pojawić się dane generowane w metodzie logMethodCall() interceptora (rysunek 7.24).
Rysunek 7.24. Dane wyjściowe z metody logMethodCall()
Usługi zegara w ziarnach EJB W bezstanowych ziarnach sesyjnych i ziarnach sterowanych komunikatami (jest to inny rodzaj ziaren EJB, opisany w następnym rozdziale) dostępna jest metoda automatycznie wykonywana w regularnych odstępach czasu. Metoda ta jest przydatna, gdy programista chce okresowo (np. co tydzień, codziennie, co godzinę itd.) uruchamiać pewien kod bez konieczności wywoływania konkretnych metod. Efekt ten można uzyskać za pomocą usług zegara ziaren EJB. 242
Rozdział 7. • Implementowanie warstwy biznesowej
Aby wykorzystać usługi zegara, należy dodać adnotację @Schedule w miejscu, gdzie dana metoda ma być wywoływana. W poniższym przykładzie pokazano, jak używać usług zegara w ziarnach EJB: package com.ensode.ejbtimer.ejb; import java.util.Date; import javax.ejb.Stateless; import javax.ejb.LocalBean; import javax.ejb.Schedule; @Stateless @LocalBean public class EJBTimerDemo { // Tu dodaj logikę biznesową. Kliknij prawym przyciskiem myszy i wybierz opcję // Insert Code/Add Business Method @Schedule(hour = "*", minute = "*", second = "*/30") public void logMessage() { System.out.println("Metoda logMessage() uruchomiona o godzinie: " + new Date(System.currentTimeMillis())); } }
W tym przykładzie do jednej z metod w ziarnie EJB dodano adnotację @Schedule. Wartość "*" w atrybucie hour oznacza, że metoda ma być uruchamiana w każdej godzinie, a wartość "*" w atrybucie minute powoduje wykonywanie metody w każdej minucie. Wartość "*/30" atrybutu second sprawia, że metoda będzie wykonywana co 30 sekund. Adnotacja @Schedule ma podobną składnię co narzędzie cron stosowane w Uniksie i innych systemach uniksowych (np. w Linuksie). Dobre wprowadzenie do tego narzędzia znajdziesz pod adresem http://www.unixgeeks.org/security/newbie/unix/cron-1.html. Po zainstalowaniu i uruchomieniu projektu w środowisku NetBeans powinieneś zobaczyć w konsoli wyjściowej dane z serwera GlassFish (rysunek 7.25).
Rysunek 7.25. Dane generowane z wykorzystaniem usług zegara
243
Java EE 6. Tworzenie aplikacji w NetBeans 7
Generowanie ziaren sesyjnych na podstawie encji JPA Bardzo atrakcyjną cechą środowiska NetBeans jest to, że umożliwia generowanie bezstanowych ziaren sesyjnych na podstawie istniejących encji JPA. Wygenerowane ziarna sesyjne działają jak obiekty DAO. Ten mechanizm, w połączeniu z możliwością generowania encji JPA na podstawie schematu istniejącej bazy danych, pozwala generować w aplikacji warstwy dostępu do danych bez konieczności pisania choćby jednego wiersza kodu w Javie. Aby wykorzystać ten mechanizm, trzeba utworzyć projekt EJB (wybierz opcję File/New Project, otwórz Enterprise na liście Categories, a następnie zaznacz EJB Module na liście Projects; możesz też wykorzystać projekt EJB z projektu typu Enterprise Application). Następnie należy dodać do niego encje JPA. Można to zrobić ręcznie lub przez wygenerowanie ich na podstawie istniejącego schematu, co opisano w rozdziale 6. Gdy w projekcie dostępne są już encje JPA, należy wybrać opcję File/New File, otworzyć Persistence na liście Categories, a następnie zaznaczyć Session Beans For Entity Classes na liście File Types (rysunek 7.26).
Rysunek 7.26. Generowanie ziaren sesyjnych na podstawie encji
Na następnej stronie kreatora można wybrać istniejące klasy encji JPA z projektu, które mają posłużyć do wygenerowania ziaren sesyjnych. W większości sytuacji ziarna tworzy się dla wszystkich encji. Aby to zrobić, wystarczy kliknąć przycisk Add All (rysunek 7.27).
244
Rozdział 7. • Implementowanie warstwy biznesowej
Rysunek 7.27. Wybieranie encji, na podstawie których powstaną ziarna
Na ostatnim ekranie kreatora można podać projekt i pakiet oraz określić, czy wygenerowane mają zostać interfejsy lokalne i (lub) zdalne (rysunek 7.28).
Rysunek 7.28. Konfigurowanie procesu generowania ziaren
245
Java EE 6. Tworzenie aplikacji w NetBeans 7
Gdy klikniesz przycisk Finish, ziarna sesyjne zostaną utworzone i zapisane w podanym pakiecie. Wszystkie generowane ziarna sesyjne dziedziczą po klasie AbstractFacade. Jest to abstrakcyjna klasa bazowa, także generowana przez kreator New Session Beans For Entity Classes. Klasa ta zawiera zestaw metod, które umożliwiają wykonywanie operacji CRUD na wskazanych encjach. package com.ensode.ejbdao.sessionbeans; import java.util.List; import javax.persistence.EntityManager; public abstract class AbstractFacade { private Class entityClass; public AbstractFacade(Class entityClass) { this.entityClass = entityClass; } protected abstract EntityManager getEntityManager(); public void create(T entity) { getEntityManager().persist(entity); } public void edit(T entity) { getEntityManager().merge(entity); } public void remove(T entity) { getEntityManager().remove(getEntityManager().merge(entity)); } public T find(Object id) { return getEntityManager().find(entityClass, id); } public List findAll() { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); return getEntityManager().createQuery(cq).getResultList(); } public List findRange(int[] range) { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); javax.persistence.Query q = getEntityManager(). createQuery(cq); q.setMaxResults(range[1] - range[0]); q.setFirstResult(range[0]); return q.getResultList(); } public int count() { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); javax.persistence.criteria.Root rt = cq.from(entityClass); cq.select(getEntityManager().getCriteriaBuilder().count(rt)); javax.persistence.Query q = getEntityManager().
246
Rozdział 7. • Implementowanie warstwy biznesowej
createQuery(cq); return ((Long) q.getSingleResult()).intValue(); } }
Jak widać, klasa AbstractFacade jest nakładką dla klasy EntityManager. Umieszczenie wywołań klasy EntityManager w ziarnie sesyjnym pozwala wykorzystać zalety ziaren (np. zarządzanie transakcjami i podział kodu na warstwy). Wygenerowana metoda create() służy do tworzenia nowych encji, metoda edit() aktualizuje istniejącą encję, a metoda remove() usuwa encję. Metoda find() wyszukuje encję o podanym kluczu głównym, a metoda findAll() zwraca kolekcję List z wszystkimi encjami z bazy. Metoda findRange() pozwala pobrać podzbiór encji z bazy. Jedynym parametrem tej metody jest tablica liczb całkowitych. Pierwszym elementem tej tablicy jest indeks pierwszej pobieranej encji, w drugim — indeks ostatniej pobieranej encji. Metoda count() zwraca liczbę encji w bazie danych i działa podobnie jak instrukcja SELECT count(*) FROM NAZWA_TABELI w standardowym SQL-u. Wcześniej wspomniano, że wszystkie wygenerowane ziarna sesyjne dziedziczą po klasie Abstract Facade. Przyjrzyj się jednemu z wygenerowanych ziaren EJB: package com.ensode.ejbdao.sessionbeans; import com.ensode.ejbdao.entities.Customer; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Stateless public class CustomerFacade extends AbstractFacade { @PersistenceContext(unitName = "EjbDaoPU") private EntityManager em; protected EntityManager getEntityManager() { return em; } public CustomerFacade() { super(Customer.class); } }
Jak widać, wygenerowane ziarno sesyjne jest bardzo proste. Zawiera zmienną egzemplarza typu EntityManager i wykorzystuje mechanizm wstrzykiwania zasobów w celu jej zainicjowania. W ziarnie dostępna jest też metoda getEntityManager(). Jest ona przeznaczona do wywoływania w klasie bazowej, która uzyskuje dzięki temu dostęp do obiektu typu EntityManager z danego ziarna sesyjnego. Ponadto w konstruktorze ziarna sesyjnego wywoływany jest konstruktor klasy bazowej, który za pomocą metod generycznych inicjuje zmienną egzemplarza entityClass w klasie bazowej. Do wygenerowanego ziarna sesyjnego można oczywiście dodać dowolne metody. Czasem potrzebna jest metoda do wyszukiwania wszystkich encji spełniających określone kryteria — na przykład wszystkich klientów o określonym nazwisku.
247
Java EE 6. Tworzenie aplikacji w NetBeans 7
Wadą dodawania metod do wygenerowanych ziaren sesyjnych jest to, że jeśli z jakichś przyczyn trzeba będzie ponownie wygenerować ziarno, utracisz niestandardowe metody i będziesz musiał ponownie je napisać. Aby uniknąć takich problemów, warto utworzyć klasy pochodne od wygenerowanego ziarna sesyjnego i umieścić dodatkowe metody w tych klasach (w Javie EE 5 ziarna sesyjne mogą dziedziczyć po sobie). Dzięki temu dodane metody nie zostaną usunięte przy ponownym generowaniu ziaren sesyjnych.
Podsumowanie W tym rozdziale zapoznałeś się z wprowadzeniem do ziaren sesyjnych i dowiedziałeś się, w jaki sposób środowisko NetBeans pomaga przyspieszyć tworzenie takich ziaren. Zobaczyłeś, jak ziarna EJB (na ogólnym poziomie) i ziarna sesyjne (na poziomie szczegółowym) pozwalają łatwo implementować strategie obsługi transakcji w dużych aplikacjach dla firm. Ponadto opisano tu, jak stosować programowanie aspektowe (ang. Aspect Oriented Programming — AOP) za pomocą ziaren sesyjnych i interceptorów. Oprócz tego dowiedziałeś się, że jedną z metod ziarna sesyjnego można wywoływać okresowo z poziomu kontenera EJB. Służą do tego usługi zegara ziaren EJB. W końcowej części rozdziału zobaczyłeś, w jaki sposób środowisko NetBeans przyspiesza tworzenie w aplikacjach warstwy dostępu do danych, umożliwiając automatyczne zaimplementowanie wzorca projektowego DAO.
248
8 Interfejs API CDI Interfejs CDI (ang. Contexts and Dependency Injection) to nowość w specyfikacji Javy EE. Interfejs ten można wykorzystać do uproszczenia integrowania różnych warstw aplikacji w Javie EE. Można na przykład zastosować ziarno sesyjne jako ziarno zarządzane, co pozwala wykorzystać mechanizmy ziaren EJB (takie jak obsługę transakcji) bezpośrednio w ziarnach zarządzanych. W tym rozdziale opisano następujące zagadnienia: wprowadzenie do interfejsu CDI; kwalifikatory; stereotypy; typy do wiązania interceptorów.
Wprowadzenie do CDI Aby móc wykorzystać funkcje interfejsu CDI w projekcie aplikacji sieciowej, wystarczy zaznaczyć pole wyboru Enable Contexts and Dependency Injection na drugiej stronie kreatora New Web Application (rysunek 8.1). W większości sytuacji używana jest także platforma JSF 2.0, ponieważ w aplikacjach CDI platformą komponentów interfejsu użytkownika jest zwykle właśnie JSF (rysunek 8.2).
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 8.1. Dodawanie obsługi interfejsu CDI do aplikacji sieciowej
Rysunek 8.2. Platformą w aplikacjach CDI jest zwykle JSF
250
Rozdział 8. • Interfejs API CDI
Zaznaczenie pola wyboru Enable Contexts and Dependency Injection powoduje utworzenie pliku beans.xml i umieszczenie go w katalogu WEB-INF aplikacji sieciowej. Wygenerowany plik beans.xml wygląda tak:
Gdy aplikacja jest instalowana, plik ten jest dla serwera aplikacji informacją, że aplikacja obsługuje interfejs CDI. W aplikacjach CDI, podobnie jak w standardowych aplikacjach JSF, zwykle do obsługi widoków stosuje się facelety. Poniżej przedstawiono typowy kod strony CDI:
Dodaj nowego klienta
Dodaj nowego klienta
251
Java EE 6. Tworzenie aplikacji w NetBeans 7
Jak widać, kod ten nie różni się znacznie od kodu aplikacji JSF bez obsługi CDI. Wygląd tej strony (po wprowadzeniu danych) przedstawia rysunek 8.3.
Rysunek 8.3. Przedstawiona strona w przeglądarce
W kodzie strony używane są komponenty JSF z wyrażeniami w języku UEL (ang. Unified Expression Language), co pozwala powiązać komponenty z właściwościami i metodami ziarna zarządzanego. Tu jednak ziarno zarządzane nie jest ziarnem JSF, ale ziarnem CDI. Przyjrzyj się najpierw ziarnu reprezentującemu klienta. package com.ensode.model; import java.io.Serializable; import javax.enterprise.context.RequestScoped; import javax.inject.Named; @Named @RequestScoped public class Customer implements Serializable { private String firstName; private String middleName; private String lastName; private String email; public Customer() { } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getMiddleName() { return middleName; } public void setMiddleName(String middleName) { this.middleName = middleName; }
252
Rozdział 8. • Interfejs API CDI
public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
W adnotacji @Named określono, że klasa ta to ziarno nazwane CDI. Domyślnie nazwą ziarna jest nazwa klasy, w której pierwsza litera jest mała (tu nazwą ziarna jest customer, ponieważ nazwa klasy to Customer). Można to zmienić. W tym celu wystarczy przypisać pożądaną nazwę do atrybutu value adnotacji @Named: @Named(value="customerBean")
Dostęp do metod i właściwości ziarna nazwanego CDI można uzyskać za pomocą faceletu, tak jak w zwykłym ziarnie zarządzanym JSF. Facelety to domyślna technologia obsługi widoków w JSF 2.0. Ich szczegółowe omówienie znajdziesz w rozdziale 4.
Ziarna nazwane CDI, podobnie jak ziarna zarządzane JSF, mogą mieć jeden z kilku zasięgów. Przedstawione wcześniej ziarno ma zasięg żądania, na co wskazuje adnotacja @RequestScoped. Możliwe zasięgi przedstawia tabela 8.1. Jak widać, w CDI dostępne są wszystkie zasięgi z JSF. Ponadto w CDI można używać dwóch innych zasięgów. Pierwszy z nich to zasięg wymiany komunikatów. Może on obejmować grupę żądań, ale jest krótszy niż zasięg sesji. Drugi zasięg charakterystyczny dla CDI to zasięg ziaren zależnych. Tak naprawdę nie jest to prawdziwy zasięg. Ziarna CDI o tym zasięgu to obiekty zależne od innych obiektów. Takie ziarna są tworzone, gdy tworzony jest obiekt, do którego należą. W momencie usunięcia obiektu usuwane jest też zależne od niego ziarno. W tworzonej aplikacji występują dwa ziarna nazwane CDI. Wcześnie zapoznałeś się już z ziarnem klienta. Drugim ziarnem nazwanym w aplikacji jest ziarno kontrolera: package com.ensode.controller; import com.ensode.model.Customer; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; @Named
253
Java EE 6. Tworzenie aplikacji w NetBeans 7
Tabela 8.1. Możliwe zasięgi ziaren Zasięg
Adnotacja
Opis
Żądania
@RequestScoped
Ziarna o zasięgu żądania są współużytkowane przez czas przetwarzania pojedynczego żądania. Tym żądaniem może być żądanie HTTP, wywołanie metody ziarna EJB, wywołanie usługi sieciowej lub przesłanie komunikatu JSM do ziarna sterowanego komunikatami.
Sesji
@SessionScoped
Ziarna o zasięgu sesji są dostępne we wszystkich żądaniach w danej sesji HTTP. Każdy użytkownik aplikacji otrzymuje własny egzemplarz ziarna o tym zasięgu.
Aplikacji
@ApplicationScoped
Ziarna o zasięgu aplikacji są dostępne przez cały czas działania programu. Ziarna o tym zasięgu są współużytkowane między sesjami użytkowników.
Wymiany komunikatów
@ConversationScoped
Zasięg wymiany komunikatów może obejmować kilka żądań, jednak zwykle jest krótszy niż zasięg sesji.
Ziarna zależne @Dependent
Ziarna zależne nie są współużytkowane. Gdy wstrzykiwane jest ziarno o tym zasięgu, tworzony jest jego nowy egzemplarz.
@RequestScoped public class Controller { @Inject private Customer customer; public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public String navigateToConfirmation() { // W prawdziwej aplikacji w tym miejscu należałoby zapisać // dane nowego klienta w bazie return "confirmation"; } }
W tej klasie w czasie wykonywania programu wstrzykiwany jest obiekt klasy Customer. Dzieje się tak dzięki adnotacji @Inject. Umożliwia ona łatwe wstrzykiwanie zależności w aplikacjach CDI. Metoda navigateToConfirmation() w przedstawionej klasie jest wywoływana, gdy użytkownik kliknie na stronie przycisk Prześlij. Metoda ta działa jak jej odpowiednik w ziarnie zarządzanym JSF, a więc zwraca łańcuch znaków, na podstawie którego aplikacja przechodzi do odpowiedniej strony. Podobnie jak w JSF, nazwą docelowej strony domyślnie jest wartość zwrócona przez tę metodę i rozszerzenie XHTML. Jeśli w przykładowym kodzie metoda navigate ToConfirmation() nie zgłosi żadnych wyjątków, użytkownik zostanie skierowany do strony confirmation.xhtml.
254
Rozdział 8. • Interfejs API CDI
Operacja zakończona powodzeniem
Aplikacja z powodzeniem dodała nowego klienta.
Nie trzeba robić nic specjalnego, aby uzyskać dostęp do właściwości ziarna nazwanego w przedstawionym kodzie. Ziarno działa tak, jakby było ziarnem zarządzanym JSF. Strona z przedstawionego kodu wygląda tak jak na rysunku 8.4.
Rysunek 8.4. Strona confirmation.xhtml w przeglądarce
Jak widać, aplikacje CDI działają tak jak aplikacje JSF, przy czym mają kilka dodatkowych zalet. Wspomniano już, że ziarna CDI obsługują dodatkowe zasięgi niedostępne w ziarnach JSF. Ponadto używanie interfejsu CDI pozwala oddzielić kod w Javie od kodu interfejsu API JSF. W CDI można też używać ziaren sesyjnych tak jak ziaren nazwanych. 255
Java EE 6. Tworzenie aplikacji w NetBeans 7
Kwalifikatory Czasem typ ziarna wstrzykiwanego do kodu to interfejs lub nadklasa Javy, jednak programista tak naprawdę chce wstrzyknąć podklasę lub klasę z implementacją danego interfejsu. W takich sytuacjach w CDI można wykorzystać kwalifikatory do określenia typu wstrzykiwanego do kodu. Kwalifikator CDI to adnotacja, do której trzeba dodać adnotację @Qualifier. Kwalifikator można zastosować do konkretnej podklasy lub określonego interfejsu. W tym podrozdziale zobaczysz, jak utworzyć kwalifikator Premium dla ziarna klienta. Klienci z takim kwalifikatorem mogą otrzymywać specjalne oferty (np. z rabatami), niedostępne dla zwykłych klientów. Tworzenie kwalifikatorów CDI w środowisku NetBeans jest bardzo proste. Wystarczy wybrać opcję File/New File, otworzyć kategorię Contexts and Dependency Injection, a następnie kliknąć typ pliku Qualifier Type (rysunek 8.5).
Rysunek 8.5. Tworzenie kwalifikatora
Na następnej stronie kreatora należy podać nazwę kwalifikatora i jego pakiet (rysunek 8.6). Gdy wykonasz te dwie proste czynności, środowisko NetBeans wygeneruje kod kwalifikatora: package com.ensode.qualifier; import import import import
256
static static static static
java.lang.annotation.ElementType.TYPE; java.lang.annotation.ElementType.FIELD; java.lang.annotation.ElementType.PARAMETER; java.lang.annotation.ElementType.METHOD;
Rozdział 8. • Interfejs API CDI
Rysunek 8.6. Konfigurowanie kwalifikatora import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.inject.Qualifier; @Qualifier @Retention(RUNTIME) @Target({METHOD, FIELD, PARAMETER, TYPE}) public @interface Premium { }
Kwalifikatory to standardowe adnotacje Javy. Zwykle obowiązują przez cały czas wykonywania programu i można je stosować do metod, pól, parametrów i typów. Jedyna różnica między kwalifikatorem a standardową adnotacją polega na tym, że same kwalifikatory mają adnotację @Qualifier. Po utworzeniu kwalifikatora należy zastosować go do konkretnej podklasy lub implementacji interfejsu: package com.ensode.model; import com.ensode.qualifier.Premium; import javax.enterprise.context.RequestScoped; import javax.inject.Named; @Named @RequestScoped @Premium
257
Java EE 6. Tworzenie aplikacji w NetBeans 7
public class PremiumCustomer extends Customer { private Integer discountCode; public Integer getDiscountCode() { return discountCode; } public void setDiscountCode(Integer discountCode) { this.discountCode = discountCode; } }
Po dodaniu adnotacji do konkretnego obiektu można zacząć stosować kwalifikatory w kodzie klienta, aby określić potrzebny typ obiektu: package com.ensode.controller; import com.ensode.model.Customer; import com.ensode.model.PremiumCustomer; import com.ensode.qualifier.Premium; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; @Named @RequestScoped public class PremiumCustomerController { private static final Logger logger = Logger.getLogger( PremiumCustomerController.class.getName()); @Inject @Premium private Customer customer; public String saveCustomer() { PremiumCustomer premiumCustomer = (PremiumCustomer) customer; logger.log(Level.INFO, "Zapisywanie informacji: \n" + "{0} {1}, discount code = {2}", new Object[]{premiumCustomer.getFirstName(), premiumCustomer.getLastName(), premiumCustomer.getDiscountCode()}); // W prawdziwej aplikacji w tym miejscu należałoby zapisać // dane klienta w bazie return "premium_customer_confirmation"; } }
Zastosowano tu kwalifikator @Premium dla pola customer, dlatego do tego pola wstrzykiwany jest obiekt klasy PremiumCustomer, ponieważ klasa ta także jest opatrzona kwalifikatorem @Premium. Na stronach JSP można uzyskać dostęp do ziarna nazwanego w standardowy sposób, używając nazwy:
258
Rozdział 8. • Interfejs API CDI
Dodawanie nowego klienta VIP
Dodawanie nowego klienta
Tu używana jest domyślna nazwa ziarna, czyli nazwa klasy z pierwszą literą zmienioną na małą. Na tym etapie można przetestować aplikację, co przedstawia rysunek 8.7. Gdy prześlesz tę stronę, zobaczysz stronę z potwierdzeniem (rysunek 8.8).
259
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 8.7. Działanie aplikacji z kwalifikatorami
Rysunek 8.8. Strona z potwierdzeniem
Stereotypy Stereotypy w CDI to mechanizm umożliwiający tworzenie nowych adnotacji łączących inne adnotacje CDI. Na przykład, jeśli chcesz utworzyć kilka ziaren nazwanych CDI o zasięgu sesji, musisz w każdym z tych ziaren podać dwie adnotacje (@Named i @SessionScoped). Zamiast dodawać te dwie adnotacje do każdego ziarna, możesz utworzyć stereotyp, a następnie zastosować go do ziaren. Aby utworzyć stereotyp CDI w środowisku NetBeans, najpierw musisz utworzyć nowy plik. Wybierz kategorię Contexts and Dependency Injection i typ pliku Stereotype (rysunek 8.9).
260
Rozdział 8. • Interfejs API CDI
Rysunek 8.9. Tworzenie stereotypu CDI
Następnie należy określić nazwę i pakiet dla nowego stereotypu (rysunek 8.10).
Rysunek 8.10. Określanie nazwy i pakietu stereotypu
261
Java EE 6. Tworzenie aplikacji w NetBeans 7
Wtedy środowisko NetBeans wygeneruje kod widoczny na rysunku 8.11.
Rysunek 8.11. Wygenerowany kod stereotypu
Teraz wystarczy dodać adnotacje CDI potrzebne w klasach, dla których dany stereotyp ma być stosowany. Tu stereotyp ma tworzyć ziarna nazwane o zasięgu sesji, dlatego należy dodać adnotacje @Named i @SessionScoped (rysunek 8.12).
Rysunek 8.12. Dodawanie potrzebnych adnotacji do stereotypu
Następnie stereotyp można wykorzystać w kodzie: package com.ensode.beans; import com.ensode.stereotype.NamedSessionScoped; import java.io.Serializable; @NamedSessionScoped public class StereotypeClient implements Serializable { private String property1; private String property2;
262
Rozdział 8. • Interfejs API CDI
public String getProperty1() { return property1; } public void setProperty1(String property1) { this.property1 = property1; } public String getProperty2() { return property2; } public void setProperty2(String property2) { this.property2 = property2; } }
Do powyższej klasy zastosowano stereotyp NamedSessionScoped. Jest to odpowiednik dodania adnotacji @Named i @SessionScoped.
Typy do wiązania interceptorów Jedną z zalet ziaren EJB jest to, że umożliwiają łatwe programowanie aspektowe z wykorzystaniem interceptorów. Interfejs CDI umożliwia tworzenie typów do wiązania interceptorów, które pozwalają powiązać interceptory z ziarnami bez wprowadzania bezpośrednich zależności ziaren od interceptora. Typy do wiązania interceptorów to adnotacje, dla których zastosowano adnotację @InterceptorBinding. Tworzenie typu do wiązania interceptorów w środowisku NetBeans wymaga utworzenia nowego pliku typu Interceptor Binding Type z kategorii Contexts and Dependency Injection (rysunek 8.13).
Rysunek 8.13. Tworzenie typu do wiązania interceptorów
263
Java EE 6. Tworzenie aplikacji w NetBeans 7
Następnie należy podać nazwę klasy i wybrać lub wpisać pakiet dla nowego typu do wiązania interceptorów (rysunek 8.14).
Rysunek 8.14. Konfigurowanie nowego typu do wiązania interceptorów
Na tym etapie środowisko NetBeans generuje kod typu do wiązania interceptorów (rysunek 8.15).
Rysunek 8.15. Wygenerowany kod nowego typu do wiązania interceptorów
Wygenerowany kod jest w pełni funkcjonalny. Nie trzeba dodawać do niego żadnych elementów. Aby wykorzystać ten typ, trzeba napisać interceptor i dodać do niego adnotację z typem do wiązania interceptorów:
264
Rozdział 8. • Interfejs API CDI
package com.ensode.interceptor; import com.ensode.interceptorbinding.LoggingInterceptorBinding; import java.util.logging.Level; import java.util.logging.Logger; import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; @LoggingInterceptorBinding @Interceptor public class LoggingInterceptor { private static final Logger logger = Logger.getLogger( LoggingInterceptor.class.getName()); @AroundInvoke public Object logMethodCall(InvocationContext invocationContext) throws Exception { logger.log(Level.INFO, new StringBuilder("Start metody ").append( invocationContext.getMethod().getName()).append( ".").toString()); Object retVal = invocationContext.proceed(); logger.log(Level.INFO, new StringBuilder("Koniec metody ").append( invocationContext.getMethod().getName()).append( ".").toString()); return retVal; } }
Przedstawiona klasa ma adnotację z typem do wiązania interceptorów, a oprócz tego jest standardowym interceptorem, takim jak te używane w ziarnach sesyjnych EJB (szczegółowy opis tego zagadnienia znajdziesz w rozdziale 7.). Aby typ do wiązania interceptorów działał prawidłowo, trzeba zarejestrować interceptor w pliku beans.xml:
com.ensode.interceptor.LoggingInterceptor
Jak widać, w celu zarejestrowania interceptora wystarczy w pliku beans.xml podać znacznik z zagnieżdżonym znacznikiem z pełną nazwą interceptora. Ostatni krok przed rozpoczęciem używania typu do wiązania interceptorów polega na dodaniu do przechwytywanej klasy adnotacji z tym typem:
265
Java EE 6. Tworzenie aplikacji w NetBeans 7
package com.ensode.controller; import com.ensode.interceptorbinding.LoggingInterceptorBinding; import com.ensode.model.Customer; import com.ensode.model.PremiumCustomer; import com.ensode.qualifier.Premium; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; @LoggingInterceptorBinding @Named @RequestScoped public class PremiumCustomerController { private static final Logger logger = Logger.getLogger( PremiumCustomerController.class.getName()); @Inject @Premium private Customer customer; public String saveCustomer() { PremiumCustomer premiumCustomer = (PremiumCustomer) customer; logger.log(Level.INFO, "Zapisywanie wprowadzonych informacji: \n" + "{0} {1}, kod rabatowy = {2}", new Object[]{premiumCustomer.getFirstName(), premiumCustomer.getLastName(), premiumCustomer.getDiscountCode()}); // W prawdziwej aplikacji należałoby w tym miejscu // zapisywać dane klienta w bazie return "premium_customer_confirmation"; } }
Na tym etapie można zacząć używać interceptora. Gdy uruchomisz powyższy kod i sprawdzisz dziennik serwera GlassFish, zobaczysz, jak działa nowy typ do wiązania interceptorów (rysunek 8.16).
Rysunek 8.16. Efekt działania typu do wiązania interceptorów
Wiersze Start metody saveCustomer i Koniec metody saveCustomer dodał do dziennika interceptor bezpośrednio wywołany przez typ do wiązania interceptorów.
266
Rozdział 8. • Interfejs API CDI
Podsumowanie W tym rozdziale opisano, w jaki sposób środowisko NetBeans obsługuje interfejs CDI (ang. Contexts and Dependency Injection). Jest to nowy interfejs API Javy EE wprowadzony w Javie EE 6. W rozdziale tym zapoznałeś się z wprowadzeniem do interfejsu CDI i dodatkowymi funkcjami, które interfejs ten zapewnia (w porównaniu ze standardową platformą JSF). Omówiono tu także, jak odróżniać od siebie wstrzyknięte ziarna CDI za pomocą kwalifikatorów CDI. Ponadto opisano, jak grupować adnotacje CDI przy użyciu stereotypów. Z ostatniej części rozdziału dowiedziałeś się, że interfejs CDI udostępnia typy do wiązania interceptorów, które ułatwiają programowanie aspektowe.
267
Java EE 6. Tworzenie aplikacji w NetBeans 7
268
9 Przesyłanie komunikatów za pomocą usług JMS i ziaren sterowanych komunikatami JMS (ang. Java Messaging Service) to standardowy interfejs API Javy EE służący do obsługi komunikatów. Pozwala on na luźno powiązaną asynchroniczną komunikację między komponentami Javy EE. Środowisko NetBeans zapewnia pomoc w zakresie tworzenia aplikacji korzystających z interfejsu JMS. Generuje dużą część szablonowego kodu, co pozwala programiście skoncentrować się na logice biznesowej aplikacji. W tym rozdziale opisano następujące zagadnienia: wprowadzenie do interfejsu JMS; tworzenie dużych projektów, w których można wykorzystać interfejs JMS; tworzenie zasobów JMS w środowisku NetBeans; implementowanie producenta komunikatów JMS; implementowanie konsumenta komunikatów JMS; przetwarzanie komunikatów JMS za pomocą ziaren sterowanych komunikatami.
Java EE 6. Tworzenie aplikacji w NetBeans 7
Wprowadzenie do interfejsu JMS JMS (ang. Java Messaging Service) to standardowy interfejs API Javy EE służący do obsługi komunikatów. Pozwala on na luźno powiązaną asynchroniczną komunikację między komponentami Javy EE. Aplikacje korzystające z tego interfejsu nie komunikują się bezpośrednio między sobą. Zamiast tego producent komunikatów JMS przesyła komunikaty do docelowej lokalizacji (kolejki lub kanału JMS), a konsument JMS odbiera komunikaty z tych lokalizacji. Przy korzystaniu z interfejsu JMS można używać dwóch modeli obsługi komunikatów. Jeden z nich to PTP (ang. Point To Point). W tym podejściu komunikat JMS jest przetwarzany tylko przez jednego odbiorcę. Drugi model to „publikuj-subskrybuj”. Tu wszyscy odbiorcy, którzy zasubskrybowali określony kanał, otrzymują i przetwarzają każdy komunikat z tego kanału. W aplikacjach JMS w modelu PTP docelową lokalizacją dla komunikatów są kolejki, natomiast w modelu „publikuj-subskrybuj” używane są kanały. Architekturę JMS przedstawia rysunek 9.1.
Rysunek 9.1. Architektura JMS
Przy korzystaniu z JMS trzeba pobrać (za pomocą identyfikatora JNDI lub wstrzykiwania zależności) referencję do fabryki połączeń. Za pomocą tej fabryki można utworzyć sesję JMS i wykorzystać ją do tworzenia komunikatów JMS. W trakcie pisania kodu wysyłającego komunikaty do docelowych lokalizacji JMS trzeba przy użyciu sesji JMS utworzyć producenta komunikatów JMS. Za pomocą producenta komunikatów można wysyłać komunikaty do docelowych lokalizacji.
270
Rozdział 9. • Przesyłanie komunikatów za pomocą usług JMS
Przy pisaniu kodu do pobierania komunikatów z lokalizacji docelowych JMS trzeba przy użyciu sesji JMS utworzyć konsumenta komunikatów. Za pomocą konsumenta komunikatów można pobierać komunikaty z docelowych lokalizacji.
Tworzenie projektu i zasobów JMS W tym przykładzie należy utworzyć projekt typu Enterprise Application (rysunek 9.2).
Rysunek 9.2. Utwórz projekt typu Enterprise Application
W projekcie potrzebne będą moduł EJB i moduł klienta aplikacji (rysunek 9.3). Producentem i (lub) konsumentem komunikatów JMS może być dowolny rodzaj modułu Javy EE. Wystarczy wywołać w nim metody interfejsu JMS. Tu używany jest moduł EJB, ponieważ dalej w rozdziale tworzone jest ziarno sterowane komunikatami (jest to jeden z typów ziaren EJB). Klient aplikacji jest używany, ponieważ jest to jeden z najprostszych modułów, jakie można dodać do aplikacji typu Enterprise Application. Dzięki temu można się skoncentrować na kodzie JMS bez konieczności pisania dużych ilości pomocniczego kodu. W prawdziwych aplikacjach producentami komunikatów JMS są zazwyczaj aplikacje sieciowe lub ziarna sesyjne, a konsumentami — ziarna sterowane komunikatami.
Po utworzeniu projektu trzeba dodać kilka potrzebnych zasobów JMS: docelową lokalizację JMS (kolejkę lub kanał) oraz fabrykę połączeń JMS. Jeśli jako serwera aplikacji używasz GlassFisha, możesz utworzyć te zasoby bezpośrednio w środowisku NetBeans.
271
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 9.3. Projekt powinien zawierać moduł EJB i moduł klienta aplikacji1
Tworzenie docelowej lokalizacji JMS Docelowa lokalizacja JMS to pośrednie miejsce, gdzie producenci JMS umieszczają komunikaty, które mogą pobierać konsumenci JMS. Przy stosowaniu modelu PTP docelowymi lokalizacjami są kolejki, natomiast w modelu „publikuj-subskrybuj” używane są kanały. W przykładzie używany jest model PTP, dlatego trzeba utworzyć kolejkę komunikatów. Proces tworzenia kanałów komunikatów jest niemal identyczny. Aby utworzyć kolejkę komunikatów, trzeba wybrać opcję File/New File, kliknąć GlassFish na liście Categories, a następnie wybrać pozycję JMS Resource z listy File Types (rysunek 9.4). Następnie należy podać identyfikator JNDI kolejki. Tu zaakceptuj nazwę domyślną jms/MyQueue i domyślny typ zasobu javax.jms.Queue (rysunek 9.5).
1
Ten zrzut pochodzi z wersji NetBeans 6.9. W nowszych wersjach można osobno utworzyć projekt klienta aplikacji (File/New File/Java EE/Enterprise Application Client) i dołączyć go w kreatorze do utworzonej wcześniej aplikacji typu Enterprise Application — przyp. tłum.
272
Rozdział 9. • Przesyłanie komunikatów za pomocą usług JMS
Rysunek 9.4. Tworzenie kolejki komunikatów
Rysunek 9.5. Konfigurowanie kolejki komunikatów
273
Java EE 6. Tworzenie aplikacji w NetBeans 7
Kolejki komunikatów JMS wymagają właściwości Name. Tu wartością tej właściwości jest identyfikator JNDI kolejki bez przedrostka jms/ (rysunek 9.6).
Rysunek 9.6. Ustawianie właściwości kolejki komunikatów Opcje w sekcji Connector Resource kreatora New JMS Resource to fabryki połączeń, które można wykorzystać do utworzenia kolejki lub sesji JMS. W Javie EE łatwiej jest wstrzykiwać kolejkę lub sesję JMS bezpośrednio do kodu, zamiast tworzyć te zasoby za pomocą fabryki połączeń.
Na tym etapie kolejka JMS, pełniąca w aplikacji funkcję docelowej lokalizacji JMS, jest już gotowa. Trzeba też utworzyć fabrykę połączeń JMS. W rzeczywistości kolejka i fabryka nie są tworzone do momentu zainstalowania projektu. Kilka pierwszych kroków w procesie tworzenia fabryki połączeń nie różni się od etapów przygotowywania kolejki: wybierz opcję File/New File, otwórz kategorię GlassFish, kliknij JMS Resource, a następnie kliknij przycisk Next>. Potem wystarczy zaznaczyć javax.jms.ConnectionFactory jako typ zasobu i wprowadzić odpowiednią nazwę JNDI dla fabryki połączeń (rysunek 9.7). Wybranie javax.jms.ConnectionFactory jako typu zasobu ma pewną zaletę — umożliwia użycie tego zasobu do tworzenia połączeń zarówno dla kolejek, jak i dla kanałów. Jeśli potrzebujesz tylko kolejki lub kanału, możesz wybrać typ javax.jms.QueueConnectionFactory lub javax.jms. TopicConnectionFactory. Typ javax.jms.ConnectionFactory zapewnia większą swobodę.
274
Rozdział 9. • Przesyłanie komunikatów za pomocą usług JMS
Rysunek 9.7. Konfigurowanie fabryki połączeń
Teraz można kliknąć przycisk Finish. Można też kliknąć Next, aby ustawić dodatkowe właściwości, jednak krok ten w przypadku fabryk połączeń nie jest konieczny. Środowisko NetBeans umieszcza zasoby serwera GlassFish w pliku glassfish-resources.xml2. Gdy projekt jest instalowany na serwerze GlassFish, serwer wczytuje ten plik i tworzy zdefiniowane w nim zasoby. Aby wyświetlić zawartość tego pliku, rozwiń węzeł Server Resources w panelu Projects i kliknij dwukrotnie nazwę pliku (rysunek 9.8).
Przesyłanie komunikatów do docelowej lokalizacji Po utworzeniu fabryki połączeń i docelowej lokalizacji (kolejki lub kanału) trzeba napisać kod, który będzie wysyłał do tej lokalizacji komunikaty. W przykładzie do przesyłania komunikatów do kolejki posłuży klient aplikacji. Środowisko NetBeans automatycznie generuje dużą ilość niezbędnego szablonowego kodu. Aby wygenerować ten kod, należy utworzyć na serwerze fabrykę połączeń i docelową lokalizację. W poprzednim punkcie wspomniano, że zasoby JMS serwera GlassFish dodane przez środowisko NetBeans są tworzone dopiero w momencie instalowania aplikacji. Aby klient miał dostęp do tych zasobów, trzeba najpierw zainstalować projekt. Spowoduje to utworzenie potrzebnych zasobów. 2
Jeśli używana jest wersja serwera GlassFish starsza niż 3.1, zasoby są zapisywane w pliku sun-resources.xml — przyp. tłum.
275
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 9.8. Zawartość pliku glassfish-resources.xml
Po zainstalowaniu projektu można wygenerować kod JMS. W tym celu otwórz klasę Main (plik Main.java) projektu klienta aplikacji, kliknij kod źródłowy tej klasy prawym przyciskiem myszy i wybierz opcję Insert Code. Następnie w menu podręcznym wybierz opcję Send JMS Message (rysunek 9.9).
Rysunek 9.9. Generowanie kodu do wysyłania komunikatów
Na tym etapie trzeba wybrać docelową lokalizację komunikatów i fabrykę połączeń (rysunek 9.10). Środowisko NetBeans wykrywa docelowe lokalizacje na serwerze i udostępnia je na liście rozwijanej. Stara się też odgadnąć identyfikator JNDI fabryki połączeń, jednak w omawianym przykładzie robi to nieprawidłowo. Na rysunku 9.10 wpisany jest już poprawiony identyfikator JNDI fabryki połączeń. Następnie środowisko NetBeans generuje w kodzie dwie metody. Jedna tworzy komunikat JMS, natomiast druga wysyła go do docelowej lokalizacji. Ponadto środowisko wstrzykuje do kodu potrzebne zasoby (docelową lokalizację i fabrykę połączeń), dodając adnotację @Resource (rysunek 9.11).
276
Rozdział 9. • Przesyłanie komunikatów za pomocą usług JMS
Rysunek 9.10. Konfigurowanie kodu do wysyłania komunikatów
Rysunek 9.11. Kod wygenerowany przez środowisko NetBeans
W przykładzie nazwą metody używanej do tworzenia komunikatów jest createJMSMessage ForjmsMyQueue (dokładna nazwa tej metody zależy od nazwy docelowej lokalizacji JMS). Metoda ta zwraca obiekt z implementacją interfejsu javax.jms.Message (interfejs ten musi być zaimplementowany we wszystkich typach komunikatów JMS) i przyjmuje dwa parametry: obiekt klasy z implementacją interfejsu javax.jms.Session i obiekt zawierający treść komunikatu.
277
Java EE 6. Tworzenie aplikacji w NetBeans 7
Interfejs javax.jms.Message ma kilka interfejsów podrzędnych, które są częścią standardowego interfejsu API Javy EE. Do wysyłania komunikatów można wykorzystać jeden z tych interfejsów podrzędnych zamiast bezpośredniej implementacji interfejsu javax.jms.Message. Przegląd wszystkich standardowych interfejsów podrzędnych z Javy EE znajdziesz w tabeli 9.1. Tabela 9.1. Interfejsy podrzędne związane z przesyłaniem komunikatów Interfejs podrzędny Opis BytesMessage
Służy do przesyłania komunikatów w postaci tablicy bajtów.
MapMessage
Służy do przesyłania komunikatów w postaci par nazwa — wartość. Nazwą musi być tu obiekt typu String, a wartość musi być jednym z typów prostych lub obiektem Javy.
ObjectMessage
Służy do przesyłania komunikatów w postaci obiektów serializowanych. Obiekt serializowany to obiekt dowolnej klasy z implementacją interfejsu java.io.Serializable.
StreamMessage
Służy do przesyłania komunikatów w postaci strumienia wartości typów prostych Javy.
TextMessage
Służy do przesyłania komunikatów w postaci wartości typu String.
Spośród tych typów najczęściej używane to TextMessage i ObjectMessage. W przykładzie używany jest typ TextMessage, a pozostałe są bardzo podobne do niego. Szczegółowe informacje na temat interfejsów API dla poszczególnych typów komunikatów znajdziesz w dokumentacji JavaDoc Javy EE: http://download.oracle.com/javaee/6/api/.
Zauważ, że wygenerowana metoda sendJMSMessageToMyQueue() wywołuje metodę createJMSMessage forjmsMyQueue(). Programiści powinni wywoływać metodę sendJMSMessageToMyQueue(), zamiast bezpośrednio używać metody createJMSMessageforjmsMyQueue(). W przykładzie metoda sendJMSMessageToMyQueue() jest wywoływana w metodzie main() aplikacji. Po dodaniu potrzebnego wywołania metoda main() powinna wyglądać tak jak na rysunku 9.12.
Rysunek 9.12. Metoda main() z wywołaniem metody sendJMSMessageToMyQueue()
Na tym etapie aplikacja wysyłająca komunikaty do kolejki jest już kompletna. Można zainstalować projekt i uruchomić go. Jednak na razie nie napisano jeszcze kodu do pobierania komunikatów. Jest to następny krok, jaki należy wykonać. Ale zanim przejdziesz dalej, przyjrzyj się wygenerowanej metodzie sendJMSMessageToMyQueue(), aby lepiej zrozumieć jej działanie. Metoda ta najpierw pobiera połączenie JMS. W tym celu wywołuje metodę createConnection() wstrzykniętego obiektu javax.jms.ConnectionFactory i przypisuje uzyskane połączenie do zmiennej lokalnej typu javax.jms.Connection. 278
Rozdział 9. • Przesyłanie komunikatów za pomocą usług JMS
Po utworzeniu połączenia JMS metoda pobiera sesję JMS. W tym celu wywołuje metodę create Session() obiektu typu Connection. Metoda createSession() ma dwa parametry. Pierwszy to wartość logiczna określająca, czy tworzona sesja obsługuje transakcje. Sesja z obsługą transakcji umożliwia przesłanie do docelowej lokalizacji JMS kilku komunikatów w ramach transakcji. Aby przesłać komunikaty w transakcji, klient JMS powinien wysłać je do kolejki w standardowy sposób, a następnie wywołać metodę commit() sesji JMS. Kod generowany przez środowisko NetBeans domyślnie nie tworzy sesji JMS z obsługą transakcji. Aby utworzyć sesję z obsługą transakcji, wystarczy zmienić wartość pierwszego parametru w wywołaniu metody createSession() na true. Drugi parametr metody createSession() określa, w jaki sposób odbiorca komunikatu JMS ma potwierdzać jego otrzymanie. Parametr ten przyjmuje trzy wartości opisane w tabeli 9.2. Są one zdefiniowane jako stałe w interfejsie javax.jms.Session. Przy tworzeniu sesji z obsługą transakcji wartość drugiego parametru metody createSession() jest ignorowana. Tabela 9.2. Tryby potwierdzania otrzymania komunikatów Tryb potwierdzania
Opis
Session.AUTO_ACKNOWLEDGE
W tym trybie sesja JMS automatycznie potwierdza otrzymanie komunikatu przez klienta.
Session.CLIENT_ACKNOWLEDGE
W tym trybie odbiorca komunikatu musi jawnie wywołać metodę acknowledge() z interfejsu javax.jms.Message, aby potwierdzić otrzymanie komunikatu.
Session.DUPS_OK_ACKNOWLEDGE
W tym trybie sesja JMS w leniwy sposób potwierdza otrzymanie komunikatu przez klienta JMS. Ten tryb może powodować, że niektóre komunikaty zostaną dostarczone więcej niż raz, jednak może też zwiększyć wydajność, ponieważ sesja nie musi wykonywać pewnych zadań związanych z zapobieganiem wielokrotnemu przesłaniu komunikatu.
Spośród trzech trybów potwierdzania najczęściej używany jest Session.AUTO_ACKNOWLEDGE, ponieważ znacznie zmniejsza ilość pracy, jaką musi wykonać programista. Środowisko NetBeans przy generowaniu kodu domyślnie używa tego trybu, jednak można go zmienić, aby spełnić określone wymagania. Po przygotowaniu sesji JMS wygenerowany kod tworzy producenta komunikatów JMS. W tym celu wywołuje metodę createProducer() obiektu sesji JMS. Metoda ta przyjmuje jeden parametr — docelową lokalizację JMS. Nie jest zaskoczeniem, że w wygenerowanym kodzie parametrem tym jest wstrzyknięta kolejka komunikatów. Ostatnią operacją w metodzie jest przesłanie komunikatu do kolejki. W tym celu metoda wywołuje metodę send() utworzonego we wcześniejszym wierszu obiektu typu javax.jms.Message Producer. Metoda ta przyjmuje jako parametr obiekt klasy z implementacją interfejsu javax.jms.Message lub jednego z jego interfejsów podrzędnych. W wygenerowanym kodzie metoda do tworzenia komunikatów (createJMSMessageForjmsMyQueue()) jest wywoływana wewnątrzwierszowo, ponieważ zwraca wartość odpowiedniego typu.
279
Java EE 6. Tworzenie aplikacji w NetBeans 7
Warto zauważyć, że ciało wygenerowanej metody do wysyłania komunikatów JMS znajduje się w bloku try/finally. Większość wierszy w bloku try może zgłosić wyjątek JMSException. Jeśli tak się stanie, kod spróbuje zamknąć sesję JMS i połączenie. Operacje te trzeba wykonać także przy normalnym kończeniu pracy kodu, dlatego uzasadnione jest umieszczenie ich w bloku finally. Choć można napisać samodzielną aplikację do pobierania komunikatów z ich docelowej lokalizacji, większość aplikacji Javy EE wykorzystuje do tego ziarna sterowane komunikatami. W środowisku NetBeans można bardzo łatwo wygenerować takie ziarna.
Przetwarzanie komunikatów JMS za pomocą ziaren sterowanych komunikatami Aby utworzyć ziarno sterowane komunikatami, należy kliknąć projekt EJB i wybrać opcję File/New File, a następnie otworzyć kategorię Enterprise JavaBeans i kliknąć typ pliku MessageDriven Bean (rysunek 9.13).
Rysunek 9.13. Tworzenie pliku ziarna sterowanego komunikatami
W oknie dialogowym, które się pojawi, należy wprowadzić nazwę, pakiet i docelową lokalizację JMS dla danego ziarna sterowanego komunikatami (rysunek 9.14).
280
Rozdział 9. • Przesyłanie komunikatów za pomocą usług JMS
Rysunek 9.14. Konfigurowanie nowego ziarna
Gdy podasz wszystkie potrzebne informacje, we wskazanym pakiecie utworzone zostanie ziarno sterowane komunikatami: package com.ensode.mdb; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.Message; import javax.jms.MessageListener; /** * * @author heffel */ @MessageDriven(mappedName = "jms/myQueue", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")}) public class MessageReceiver implements MessageListener { public MessageReceiver() { } @Override public void onMessage(Message message) { } }
281
Java EE 6. Tworzenie aplikacji w NetBeans 7
W wygenerowanym kodzie wystarczy zaimplementować metodę onMessage(). Następnie można zainstalować projekt. Metoda onMessage() odpowiada za przetwarzanie wszystkich komunikatów z docelowej lokalizacji JMS używanej przez dane ziarno sterowane komunikatami. W metodzie onMessage() można umieścić dowolny kod. Możliwości są nieskończone, jednak metoda ta zwykle służy do zachowywania danych z komunikatu w bazie lub zapisywania danych wyjściowych w dzienniku. Tu zawartość komunikatu jest przesyłana do standardowego dziennika wyjścia serwera aplikacji: public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println("Otrzymany komunikat:" + textMessage.getText()); } catch (JMSException ex) { Logger.getLogger( MessageReceiverBean.class.getName()).log( Level.SEVERE, null, ex); } }
Zauważ, że parametr message trzeba rzutować na interfejs podrzędny javax.jms.TextMessage użyty w docelowej lokalizacji. Aby pobrać zawartość komunikatu, należy wywołać metodę getText() z tego interfejsu. Metoda ta może zgłaszać wyjątek JMSException, dlatego wywołanie trzeba umieścić w bloku try-catch. Środowisko NetBeans przypomina o konieczności przechwytywania wyjątków JMSException. W tym celu podkreśla problematyczny wiersz czerwoną falistą linią. Jeśli umieścisz kursor w tym wierszu i wciśniesz kombinację Alt+Enter, środowisko NetBeans automatycznie wygeneruje blok try-catch.
Teraz można wypróbować aplikację. W tym celu kliknij prawym przyciskiem myszy utworzony projekt typu Enterprise Application i wybierz opcję Run. Aplikacja zostanie zainstalowana, po czym uruchomi się projekt klienta aplikacji, który wyśle komunikat do kolejki. Instalowane są też ziarna EJB, a serwer aplikacji automatycznie przypisuje jedno z nich do przetwarzania komunikatów przesłanych do kolejki. Rysunek 9.15 przedstawia dane wyjściowe od ziarna sterowanego komunikatami zapisane w dzienniku serwera aplikacji.
Rysunek 9.15. Efekt przetwarzania komunikatów przez ziarno sterowane komunikatami
282
Rozdział 9. • Przesyłanie komunikatów za pomocą usług JMS
Jak widać, środowisko NetBeans automatyzuje większość żmudnych zadań potrzebnych do napisania aplikacji wykorzystującej interfejs API JMS. Programista musi tylko napisać logikę biznesową potrzebną w danej aplikacji. Przed przejściem do dalszych zagadnień warto omówić wygenerowany przez środowisko NetBeans kod ziaren sterowanych komunikatami. Zauważ, że do wygenerowanej klasy dodano adnotację @MessageDriven. Adnotacja ta określa, że dane ziarno EJB jest sterowane komunikatami. Atrybut mappedName w adnotacji @MessageDriven powinien obejmować identyfikator JNDI docelowej lokalizacji JMS (kolejki lub kanału) powiązanej z danym ziarnem. Wartością właściwości activationConfig musi być tablica adnotacji @ActivationConfigProperty. Adnotacja ta służy do określania, jaką wartość mają różne właściwości. W atrybucie propertyName można ustawić nazwę właściwości, a w atrybucie propertyValue — jej wartość. Przy tworzeniu ziaren sterowanych komunikatami adnotacje @ActivationConfigProperty pozwalają określić tryb potwierdzania dla ziarna (więcej na ten temat znajdziesz we wcześniejszym podrozdziale). Typem docelowej lokalizacji JMS przypisywanej do ziarna może być javax.jms.Queue (używany w modelu PTP) lub javax.jms.Topic (używany w modelu „publikujsubskrybuj”). Oprócz wymienionych adnotacji warto zwrócić uwagę na to, że wygenerowane ziarno sterowane komunikatami zawiera implementację interfejsu javax.jms.MessageListener. Interfejs ten obejmuje jedną metodę, onMessage(), i trzeba go zaimplementować w każdym ziarnie sterowanym komunikatami.
Podsumowanie W tym rozdziale opisano, jak w środowisku NetBeans tworzyć aplikacje z obsługą komunikatów za pomocą interfejsu API JMS. Dowiedziałeś się tu, jak skonfigurować serwer aplikacji, dodając zasoby JMS bezpośrednio z poziomu środowiska NetBeans. Zobaczyłeś też, że środowisko NetBeans może wygenerować większość kodu potrzebnego do wysyłania komunikatów JMS. Dzięki temu programiści mogą tylko uzupełnić brakujące elementy i napisać logikę biznesową potrzebną w aplikacji. Wyjaśniono tu także, że środowisko NetBeans może wygenerować większość kodu potrzebnego do pobierania komunikatów JMS w ziarnach sterowanych komunikatami. Także tu ręcznie trzeba dodać tylko logikę biznesową aplikacji.
283
Java EE 6. Tworzenie aplikacji w NetBeans 7
284
10 Usługi sieciowe SOAP oparte na interfejsie JAX-WS Usługi sieciowe umożliwiają tworzenie mechanizmów, do których można uzyskać dostęp przez sieć. Usługi sieciowe tym różnią się od innych podobnych technologii (np. od ziaren EJB), że są niezależne od języka i platformy. Na przykład usługa sieciowa napisana w Javie może być używana przez klienty napisane w innych językach i na odwrót. W tym rozdziale opisano następujące zagadnienia: wprowadzenie do usług sieciowych; tworzenie prostej usługi sieciowej; tworzenie klienta usługi sieciowej; udostępnianie ziaren EJB jako usług sieciowych.
Wprowadzenie do usług sieciowych Usługi sieciowe umożliwiają pisanie mechanizmów, do których można uzyskać dostęp przez sieć w sposób niezależny od języka i platformy. Istnieją dwa podstawowe modele często stosowane przy tworzeniu usług sieciowych. Pierwsze podejście polega na wykorzystaniu protokołu SOAP (ang. Simple Object Access Protocol), natomiast drugie związane jest z używaniem protokołu REST (ang. Representational State Transfer). Środowisko NetBeans obsługuje tworzenie usług sieciowych w obu tych modelach.
Java EE 6. Tworzenie aplikacji w NetBeans 7
Usługi sieciowe SOAP omówiono w tym rozdziale, a opis usług RESTful znajdziesz w rozdziale następnym. Gdy używa się protokołu SOAP, działanie usługi sieciowej należy zdefiniować w dokumencie XML zapisywanym w pliku WSDL (ang. Web Services Definition Language). Po utworzeniu pliku WSDL należy zaimplementować usługę w odpowiednim języku programowania, na przykład w Javie. Proces tworzenia plików WSDL jest skomplikowany i łatwo tu o błąd. Na szczęście w Javie EE można przy instalowaniu usługi sieciowej na serwerze aplikacji automatycznie wygenerować plik WSDL na podstawie danej usługi napisanej w Javie. Ponadto jeśli dostępny jest plik WSDL i trzeba zaimplementować w Javie działanie danej usługi sieciowej, środowisko NetBeans może automatycznie wygenerować większość kodu implementacji i utworzyć klasę z namiastkami metod odpowiadającymi każdej operacji usługi. Następnie wystarczy dodać logikę w każdej metodzie — cały kod pomocniczy jest generowany automatycznie.
Tworzenie prostej usługi sieciowej W tym podrozdziale zobaczysz, jak utworzyć usługę sieciową do przekształcania jednostek długości. Usługa ta ma przekształcać wartości z cali na centymetry i na odwrót. Aby utworzyć usługę sieciową, najpierw trzeba przygotować nowy projekt aplikacji sieciowej. Tu nosi on nazwę UnitConversion. Następnie można dodać usługę sieciową. W tym celu kliknij projekt prawym przyciskiem myszy, wybierz opcję File/New File, otwórz kategorię Web Services i kliknij typ pliku Web Service (rysunek 10.1).
Rysunek 10.1. Tworzenie usługi sieciowej
286
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Gdy klikniesz przycisk Next>, wpisz nazwę i pakiet usługi sieciowej (rysunek 10.2).
Rysunek 10.2. Określanie nazwy i pakietu usługi sieciowej
Kliknięcie przycisku Finish powoduje utworzenie usługi sieciowej, po czym środowisko automatycznie otwiera jej kod źródłowy (rysunek 10.3).
Rysunek 10.3. Wygenerowany kod usługi sieciowej
287
Java EE 6. Tworzenie aplikacji w NetBeans 7
Jak widać, środowisko NetBeans automatycznie generuje prostą usługę sieciową typu „Witaj, świecie!”. Adnotacja @WebService na poziomie klasy oznacza, że klasa ta jest usługą sieciową. Adnotacja @WebMethod na poziomie metody określa, że metoda ta odpowiada operacji usługi sieciowej. Atrybut operationName w tej adnotacji to nazwa operacji. Tą nazwą będą się posługiwać klienty usługi sieciowej. Adnotacja @WebParam służy do definiowania właściwości parametrów operacji usługi sieciowej. W wygenerowanej usłudze atrybut name określa nazwę parametru w pliku WSDL generowanym w momencie instalowania usługi. W środowisku NetBeans można modyfikować usługi sieciowe za pomocą interfejsu graficznego (rysunek 10.4). Można tu za pomocą myszy dodawać i (lub) usuwać operacje oraz parametry usługi sieciowej. Powoduje to automatyczne dodawanie odpowiednich namiastek metod i adnotacji do kodu usługi sieciowej. Aby wyświetlić graficzne okno projektowe usług sieciowych, wystarczy kliknąć przycisk Design w prawej górnej części kodu źródłowego usługi.
Rysunek 10.4. Interfejs graficzny do modyfikowania usługi sieciowej
Najpierw należy usunąć automatycznie wygenerowaną operację. Aby to zrobić, wystarczy kliknąć przycisk Remove Operation (rysunek 10.5). W celu dodania operacji usługi sieciowej wystarczy kliknąć przycisk Add Operation… i uzupełnić puste pola w wyświetlonym oknie dialogowym. Tworzona usługa sieciowa ma udostępniać dwie operacje: jedną do przekształcania wartości z cali na centymetry i drugą do przekształcania z centymetrów na cale. Obie te operacje mają przyjmować jeden parametr typu double i zwracać wartość tego samego typu. Po kliknięciu przycisku Add Operation… możesz wprowadzić potrzebne informacje na temat operacji inches ToCentimeters (rysunek 10.6).
288
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Rysunek 10.5. Za pomocą przycisku Remove Operation można usuwać operacje usługi sieciowej
Rysunek 10.6. Konfigurowanie nowej operacji
Następnie należy zrobić to samo dla operacji centimetersToInches (ten krok nie jest tu pokazany). Teraz w oknie do projektowania usługi powinny znajdować się dwie nowo dodane operacje (rysunek 10.7). Oprócz dodawania operacji do usługi sieciowej, można skonfigurować poziom jakości usług. W tym celu należy zaznaczyć odpowiednie pola wyboru w oknie projektowym. Usługi sieciowe przesyłają dane w postaci komunikatów tekstowych w formacie XML. Komunikaty te są przekazywane między usługą a jej klientem. Czasem trzeba też przesyłać dane binarne, na przykład rysunki. Dane binarne zwykle zapisuje się wewnątrzwierszowo w komunikatach SOAP.
289
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 10.7. Dwie nowe operacje usługi
Za pomocą mechanizmu MTOM (ang. Message Transmission Optimization Mechanism) dane binarne można przesłać jako załącznik komunikatu, co zwiększa wydajność przekazywania takich danych. Aby w środowisku NetBeans określić, że używany ma być mechanizm MTOM, wystarczy w oknie projektowym zaznaczyć pole wyboru Optimize Transfer Of Binary Data (MTOM). Pole wyboru Reliable Message Delivery pozwala określić, że programista chce zagwarantować, iż komunikaty będą dostarczane dokładnie raz. Włączenie niezawodnego dostarczania komunikatów pozwala aplikacjom przywrócić stan w sytuacjach, gdy komunikaty zostały utracone w trakcie przesyłania. Pole wyboru Secure Service powoduje włączenie zabezpieczeń dla usług sieciowych. Komunikaty przesyłane między klientem a serwerem są wtedy szyfrowane, a ponadto klient musi podać dane uwierzytelniające. Aby skonfigurować zabezpieczenia usług sieciowych, należy kliknąć przycisk Advanced… i wybrać odpowiednie opcje w wyświetlonym oknie dialogowym. Jeśli klikniesz zakładkę Source, zobaczysz wygenerowane namiastki metod (rysunek 10.8).
290
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Rysunek 10.8. Kod źródłowy z wygenerowanymi namiastkami metod
Teraz wystarczy zastąpić zawartość wygenerowanych metod „prawdziwym” kodem i zainstalować aplikację, aby usługa sieciowa była gotowa do użycia. Tu w kodzie należy pomnożyć liczbę cali przez 2.54, aby uzyskać liczbę centymetrów, i podzielić liczbę centymetrów przez 2.54, aby określić liczbę cali. Po zastąpieniu zawartości metod kodem wykonującym odpowiednie zadania można zainstalować usługę sieciową. W tym celu kliknij projekt prawym przyciskiem myszy i wybierz opcję Deploy.
Testowanie usługi sieciowej Na tym etapie w oknie Projects widoczny powinien być węzeł Web Services. Gdy go rozwiniesz, zobaczysz nowo utworzoną usługę sieciową (rysunek 10.9). Jeśli usługa sieciowa jest zainstalowana na serwerze aplikacji GlassFish dołączonym do środowiska NetBeans, usługę można łatwo przetestować — wystarczy kliknąć ją prawym przyciskiem myszy w oknie Projects i wybrać opcję Test Web Service. Efekt jest widoczny na rysunku 10.10.
291
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 10.9. Nowa usługa sieciowa w oknie Projects
Rysunek 10.10. Testowanie usług sieciowych
Tu można przetestować metody usługi sieciowej. Wpisz wartości w polach tekstowych i kliknij odpowiedni przycisk. Na przykład gdy wpiszesz 2.54 w drugim polu tekstowym i klikniesz przycisk centimetersToInches, w przeglądarce pojawi się strona z rysunku 10.11. W górnej części strony wyświetlane są przekazane do metody parametry i zwrócona wartość. W dolnej części znajdują się nieprzetworzone żądanie i odpowiedź SOAP.
292
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Rysunek 10.11. Testowana metoda działa zgodnie z oczekiwaniami
Tworzenie klienta usługi sieciowej Po przygotowaniu usługi sieciowej i przetestowaniu jej w celu stwierdzenia, że działa poprawnie, można utworzyć prostego klienta wywołującego tę usługę. Klientem usługi sieciowej może być dowolny projekt Javy: standardowa aplikacja Javy, aplikacja Javy ME, aplikacja sieciowa lub projekt dużej aplikacji dla firm. Aby kod klienta był prosty, utwórz zwykłą aplikację Javy (rysunek 10.12). Po przygotowaniu projektu należy utworzyć nowy plik klienta usługi sieciowej. Otwórz kategorię Web Services i wybierz typ pliku Web Service Client (rysunek 10.13). Na następnej stronie kreatora należy zaznaczyć przycisk opcji Project (jeśli jeszcze nie jest wybrany), a następnie kliknąć przycisk Browse i wybrać usługę sieciową z projektu usług. W odpowiednim polu automatycznie pojawi się adres URL pliku WSDL wygenerowanego dla wybranej usługi sieciowej (rysunek 10.14). Zauważ, że można tworzyć klienty używające usług sieciowych napisanych przez innych programistów. Aby to zrobić, należy zaznaczyć przycisk opcji Local File (w celu użycia pliku WSDL z lokalnego dysku twardego) lub WSDL URL (by wybrać plik WSDL opublikowany w internecie).
293
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 10.12. Tworzenie projektu klienta aplikacji sieciowej
Rysunek 10.13. Dodawanie pliku klienta
294
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Rysunek 10.14. Wybieranie usługi używanej przez klienta
Na tym etapie do projektu dodawany jest węzeł Web Service References. Gdy rozwiniesz ten węzeł do końca, zobaczysz operacje zdefiniowane w wybranym projekcie usług sieciowych (rysunek 10.15).
Rysunek 10.15. W węźle Web Service References widoczne są operacje usługi sieciowej
Tworzenie klienta usług sieciowych zwykle wymaga pisania dużej ilości szablonowego kodu. Jednak w środowisku NetBeans wystarczy przeciągnąć do kodu operację usługi sieciowej, którą kod ma wywoływać. Środowisko wygeneruje wtedy cały potrzebny szablonowy kod. Następnie
295
Java EE 6. Tworzenie aplikacji w NetBeans 7
wystarczy podać parametry przekazywane do usługi. Jeśli przeciągniesz operację inchesToCentimeters z okna Projects do klasy main klienta usługi sieciowej, wygenerowany zostanie kod z rysunku 10.16.
Rysunek 10.16. Kod klienta wygenerowany przez środowisko NetBeans
Jak widać, środowisko automatycznie dodało metodę inchesToCentimeters() (jest to nazwa przeciągniętej do kodu operacji usługi sieciowej). Metoda ta wywołuje kilka metod klasy UnitConversion_Service. Klasa ta (wraz z kilkoma innymi) jest automatycznie generowana w momencie przeciągania operacji usługi sieciowej do kodu. Aby zobaczyć wygenerowane klasy, należy rozwinąć węzeł Generated Sources (jax-ws) w oknie projektu (rysunek 10.17).
Rysunek 10.17. Automatycznie wygenerowane klasy
296
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Metoda getUnitConversionPort() klasy UnitConversion_Service zwraca obiekt klasy Unit Conversion wygenerowanej na podstawie pliku WSDL i podobnej do napisanej w projekcie usługi sieciowej klasy o tej samej nazwie. Metoda wygenerowana w efekcie przeciągnięcia operacji usługi sieciowej do kodu wywołuje metodę getUnitConversionPort(), a następnie wywołuje metodę inchesToCentimeters() zwróconego obiektu klasy UnitConversion. Programista musi tylko wywołać wygenerowaną metodę w metodzie main. Kod po wprowadzeniu tej prostej zmiany wygląda teraz tak jak na rysunku 10.18.
Rysunek 10.18. Kod klienta wywołuje operację usługi sieciowej
Teraz można uruchomić kod klienta usługi sieciowej. W konsoli powinny pojawić się informacje z rysunku 10.19.
Rysunek 10.19. Dane zwrócone do klienta przez usługę sieciową
297
Java EE 6. Tworzenie aplikacji w NetBeans 7
Udostępnianie ziaren EJB jako usług sieciowych W poprzednim przykładzie dotyczącym usługi sieciowej zobaczyłeś, jak udostępnić obiekt POJO (ang. Plain Old Java Object) jako usługę sieciową. Wymagało to umieszczenia obiektu w aplikacji sieciowej i dodania kilku adnotacji. W ten sposób można bardzo łatwo tworzyć usługi sieciowe instalowane w aplikacjach sieciowych. W projekcie modułu EJB jako usługę sieciową można udostępnić bezstanowe ziarno sesyjne. Dzięki temu ziarna można używać w klientach napisanych w językach innych niż Java. Udostępnianie bezstanowych ziaren sesyjnych jako usług sieciowych powoduje, że w usługach można wykorzystać wszystkie funkcje dostępne w ziarnach EJB (np. zarządzanie transakcjami i programowanie aspektowe). Ziarno sesyjne można udostępnić jako usługę sieciową dwoma sposobami. Gdy tworzysz nową usługę sieciową w projekcie modułu EJB, usługa ta jest automatycznie implementowana jako bezstanowe ziarno sesyjne. Ponadto istniejące ziarna sesyjne z projektu modułu EJB można udostępnić jako usługi sieciowe.
Tworzenie nowych usług sieciowych jako ziaren EJB Aby utworzyć nową usługę sieciową jako ziarno EJB, wystarczy utworzyć nową usługę sieciową w projekcie modułu EJB. W tym celu kliknij projekt prawym przyciskiem myszy i wybierz opcję New/Web Service (rysunek 10.20).
Rysunek 10.20. Tworzenie nowej usługi sieciowej w projekcie modułu EJB
298
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
W kreatorze usług sieciowych należy podać nazwę usługi i pakiet z kodem usługi, zaznaczyć przycisk opcji Create Web Service From Scratch, a następnie kliknąć przycisk Finish, aby wygenerować usługę. Na tym etapie powinieneś zobaczyć kod źródłowy usługi sieciowej (rysunek 10.21).
Rysunek 10.21. Kod źródłowy wygenerowanej usługi sieciowej
Zauważ, że w wygenerowanym ziarnie sesyjnym nie ma implementacji lokalnego ani zdalnego interfejsu. Ziarno ma natomiast adnotację @WebService, a do metod ziarna dodano adnotację @WebMethod. Ponadto każdy parametr ma adnotację @WebParam. Jedyna różnica między wygenerowanym kodem tej usługi sieciowej a kodem usługi z poprzedniego przykładu polega na tym, że tu wygenerowana klasa jest bezstanowym ziarnem sesyjnym, dzięki czemu można w niej korzystać z zarządzania transakcjami, programowania aspektowego i innych mechanizmów ziaren EJB. Usługę sieciową zaimplementowaną jako ziarno sesyjne można (podobnie jak zwykłe usługi sieciowe) projektować za pomocą wizualnego okna projektowego środowiska NetBeans. Tu po usunięciu automatycznie wygenerowanej operacji i dodaniu dwóch potrzebnych operacji okno projektowe powinno wyglądać tak jak na rysunku 10.22. Gdy klikniesz zakładkę Source, zobaczysz nowo wygenerowane metody wraz z odpowiednimi adnotacjami (rysunek 10.23). Po zainstalowaniu projektu klienty będą mogły używać tej usługi sieciowej jak dowolnej innej. Dla klienta nie ma znaczenia, że usługa sieciowa jest zaimplementowana jako ziarno sesyjne.
299
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 10.22. Okno projektowe nowej usługi sieciowej
Rysunek 10.23. Kod nowych metod usługi sieciowej
300
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Udostępnianie istniejących ziaren EJB jako usług sieciowych Druga metoda udostępniania ziaren EJB jako usług sieciowych polega na użyciu do tego istniejącego ziarna EJB. Należy w standardowy sposób utworzyć usługę sieciową (za pomocą opcji File/New/Web Service), a następnie podać nazwę i pakiet usługi sieciowej oraz zaznaczyć przycisk opcji Create Web Service from Existing Session Bean. Później należy wskazać ziarno sesyjne udostępniane jako usługa sieciowa. W tym celu kliknij przycisk Browse… i wybierz odpowiednie ziarno (rysunek 10.24).
Rysunek 10.24. Konfigurowanie usługi sieciowej opartej na istniejącym ziarnie EJB
Gdy klikniesz przycisk Finish, środowisko utworzy nową usługę sieciową i automatycznie wyświetli jej kod źródłowy (rysunek 10.25). Jak widać, tworzenie usługi sieciowej na podstawie istniejącego ziarna sesyjnego prowadzi do powstania nowego bezstanowego ziarna sesyjnego. To nowe ziarno jest klientem istniejącego ziarna EJB, na co w przykładzie wskazuje zmienna egzemplarza ejbRef z adnotacją @EJB. Gdy klikniesz przycisk Design w górnej części strony, zobaczysz wizualne okno projektowe nowo utworzonej usługi sieciowej (rysunek 10.26).
301
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 10.25. Kod źródłowy wygenerowanej usługi sieciowej
Rysunek 10.26. Okno projektowe usługi sieciowej
302
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Ziarna EJB można udostępniać jako usługi sieciowe także w projektach aplikacji sieciowych. Wtedy wygenerowana usługa sieciowa to obiekt POJO z adnotacjami @WebService, @WebMethod i @WebParam oraz metodami pomocniczymi, które wywołują odpowiednie metody ziarna EJB udostępnianego jako usługa sieciowa.
Tworzenie usługi sieciowej na podstawie istniejącego pliku WSDL Tworzenie usług sieciowych SOAP zwykle wymaga przygotowania pliku WSDL. Proces pisania takich plików jest skomplikowany i łatwo w nim o błędy, jednak na szczęście w Javie EE nie trzeba tworzyć tych plików ręcznie. Są one generowane automatycznie w momencie instalowania usługi sieciowej na serwerze aplikacji. Czasem jednak plik WSDL jest dostępny i trzeba zaimplementować opisane w nim operacje w kodzie Javy. Na potrzeby takiej sytuacji w środowisku NetBeans dostępny jest kreator, który na podstawie istniejącego pliku WSDL tworzy klasę Javy z namiastkami metod. Aby uzyskać potrzebną klasę, należy utworzyć nowy plik. Otwórz kategorię Web Services i wybierz typ pliku Web Service from WSDL (rysunek 10.27).
Rysunek 10.27. Tworzenie usługi sieciowej na podstawie pliku WSDL
303
Java EE 6. Tworzenie aplikacji w NetBeans 7
Następnie należy podać nazwę, pakiet i istniejący plik WSDL dla usługi sieciowej (rysunek 10.28).
Rysunek 10.28. Konfigurowanie usługi sieciowej
Środowisko wygeneruje wtedy namiastki metod dla wszystkich operacji zdefiniowanych we wskazanym pliku WSDL (rysunek 10.29). Teraz wystarczy dodać kod do wszystkich wygenerowanych metod. Tu wykorzystano plik WSDL wygenerowany w poprzednim przykładzie. Nowe metody są więc zbędne, ponieważ wszystkie operacje zostały już zaimplementowane. Przedstawioną procedurę można jednak zastosować do dowolnego pliku WSDL (zarówno z lokalnego systemu plików, jak i zainstalowanego na serwerze).
304
Rozdział 10. • Usługi sieciowe SOAP oparte na interfejsie JAX-WS
Rysunek 10.29. Kod wygenerowany na podstawie pliku WSDL
Podsumowanie W tym rozdziale opisano mechanizmy tworzenia usług sieciowych w środowisku NetBeans, m.in. udostępnianie metod POJO jako usług sieciowych i automatyczne dodawanie do usług potrzebnych adnotacji. Wiesz już, że środowisko NetBeans wspomaga użytkowników w tworzeniu klientów usług sieciowych — generuje większość potrzebnego szablonowego kodu, dzięki czemu programista musi tylko podać parametry przekazywane do operacji usługi. Ponadto dowiedziałeś się, jak udostępniać metody ziaren EJB jako operacje usług sieciowych, a także w jaki sposób środowisko NetBeans umożliwia i ułatwia udostępnianie zarówno nowych, jak i istniejących ziaren EJB jako usług sieciowych. Zobaczyłeś też, jak środowisko NetBeans pomaga w implementowaniu usług sieciowych na podstawie istniejących plików WSDL (zapisanych w lokalnym systemie plików lub zainstalowanych na serwerze). NetBeans generuje na podstawie danego pliku WSDL namiastki metod.
305
Java EE 6. Tworzenie aplikacji w NetBeans 7
306
11 Usługi sieciowe RESTful oparte na interfejsie JAX-RS REST (ang. Representational State Transfer) to architektura, w której usługi sieciowe są traktowane jak zasoby i identyfikowane za pomocą identyfikatorów URI (ang. Uniform Resource Identifier). Usługi sieciowe w architekturze REST to usługi typu RESTful. Java EE 6 obsługuje takie usługi za pomocą interfejsu JAX-RS (ang. Java API for RESTful Web Services). JAX-RS był od pewnego czasu dostępny jako niezależny interfejs API, a od Javy EE 6 jest częścią tej specyfikacji. Usługi sieciowe typu RESTful bardzo często stosuje się jako fronton bazy danych. Klienty takich usług mogą z nich korzystać do wykonywania operacji CRUD (ang. Create, Read, Update, Delete, czyli tworzenie, wczytywanie, aktualizowanie i usuwanie) na bazie danych. Ponieważ podejście to jest stosowane tak często, środowisko NetBeans umożliwia jego bardzo wygodne stosowanie. Usługi sieciowe typu RESTful pełniące funkcję frontonu bazy danych można utworzyć za pomocą kilku kliknięć myszą. Oto wybrane z zagadnień omawianych w tym rozdziale: generowanie usług sieciowych typu RESTful na podstawie istniejącej bazy danych; testowanie usług sieciowych typu RESTful za pomocą narzędzi środowiska NetBeans; generowanie kodu klienta usług sieciowych typu RESTful.
Java EE 6. Tworzenie aplikacji w NetBeans 7
Generowanie usług sieciowych typu RESTful na podstawie istniejącej bazy danych Aby utworzyć usługę sieciową typu RESTful na podstawie istniejącej bazy, wystarczy w projekcie aplikacji sieciowej wybrać opcję File/New File, a następnie otworzyć kategorię Web Services i kliknąć typ danych RESTful Web Services from Database (rysunek 11.1).
Rysunek 11.1. Generowanie usług sieciowych typu RESTful
Na następnej stronie kreatora trzeba wybrać źródło danych i zaznaczyć jedną lub kilka tabel, na podstawie których środowisko ma wygenerować usługę sieciową. Tu usługa jest generowana na podstawie tabeli CUSTOMER z bazy danych customerdb (rysunek 11.2). Na kolejnej stronie kreatora należy określić pakiet na kod usługi sieciowej (rysunek 11.3). Następnie należy wybrać pakiet zasobów z listy Resource Package lub zaakceptować wartość domyślną service (rysunek 11.4). Warto wprowadzić nazwę pakietu zgodną ze standardowymi konwencjami nazewnictwa. Gdy klikniesz przycisk Finish, pojawi się okno dialogowe z pytaniem o to, czy chcesz zarejestrować zasób REST (rysunek 11.5).
308
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
Rysunek 11.2. Wybieranie tabel używanych do wygenerowania usługi sieciowej
Rysunek 11.3. Ustawianie pakietu dla usługi sieciowej
309
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 11.4. Określanie pakietu zasobów
Rysunek 11.5. W tym oknie można zarejestrować zasób REST
Dostępne są tu trzy opcje: wygenerowanie podklasy klasy javax.ws.rs.core.Application; rezygnacja z wykonywania operacji (po wybraniu tej opcji należy później ręcznie
dodać kod lub konfigurację, aby zarejestrować zasoby JAX-RS); wygenerowanie deskryptora wdrażania web.xml o odpowiedniej konfiguracji.
310
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
W większości przypadków należy wybrać pierwszą opcję, ponieważ jedną z największych zalet Javy EE 6 w porównaniu ze starszymi wersjami tej specyfikacji jest mniejsza zależność od plików konfiguracyjnych (takich jak web.xml). Opcja z pominięciem operacji wymaga ręcznego skonfigurowania usług sieciowych typu RESTful, a opcja z generowaniem deskryptora web.xml oparta jest na starszym sposobie konfigurowania zasobów JAX-RS z wykorzystaniem tego deskryptora. Zwykle warto zaznaczyć pole wyboru Add Jersey Library (JAX-RS reference implementation) to project classpath, ponieważ powoduje to dodanie do projektu potrzebnych bibliotek JAX-RS.
Analiza wygenerowanego kodu Kreator opisany w poprzednim punkcie tworzy encję JPA dla każdej wybranej tabeli, a ponadto klasę AbstractFacade i klasę fasady dla każdej wygenerowanej encji JPA. Wygenerowany kod jest zgodny ze wzorcem projektowym Fasada, ponieważ każda klasa fasady jest tu nakładką na kod JPA. Więcej informacji na temat wzorca projektowego Fasada znajdziesz na stronie http://pl.wikipedia.org/wiki/ Fasada_(wzorzec_projektowy).
Wygenerowane klasy fasady są instalowane jako usługi sieciowe typu RESTful i dostępne w ten sposób (rysunek 11.6).
Rysunek 11.6. Wygenerowane klasy w projekcie AbstractFacade to klasa, po której dziedziczą wszystkie klasy fasady: package com.ensode.netbeansbook.jaxrs.service; import java.util.List; import javax.persistence.EntityManager; public abstract class AbstractFacade { private Class entityClass;
311
Java EE 6. Tworzenie aplikacji w NetBeans 7
public AbstractFacade(Class entityClass) { this.entityClass = entityClass; } protected abstract EntityManager getEntityManager(); public void create(T entity) { getEntityManager().persist(entity); } public void edit(T entity) { getEntityManager().merge(entity); } public void remove(T entity) { getEntityManager().remove(getEntityManager().merge(entity)); } public T find(Object id) { return getEntityManager().find(entityClass, id); } public List findAll() { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); return getEntityManager().createQuery(cq).getResultList(); } public List findRange(int[] range) { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); javax.persistence.Query q = getEntityManager().createQuery(cq); q.setMaxResults(range[1] - range[0]); q.setFirstResult(range[0]); return q.getResultList(); } public int count() { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); javax.persistence.criteria.Root rt = cq.from(entityClass); cq.select(getEntityManager().getCriteriaBuilder().count(rt)); javax.persistence.Query q = getEntityManager().createQuery(cq); return ((Long) q.getSingleResult()).intValue(); } }
Jak widać, klasa AbstractClass ma zmienną entityClass, która w klasach pochodnych jest ustawiana na odpowiedni typ za pomocą typów generycznych. Dostępne są tu także metody do tworzenia, edytowania, usuwania, wyszukiwania i zliczania encji. W ciele tych metod znajduje się standardowy kod JPA, który powinien być już zrozumiały.
312
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
Wcześniej wspomniano, że kreator generuje klasę fasady dla każdej utworzonej encji JPA. Tu wybrano tylko jedną tabelę (CUSTOMER), dlatego tworzona jest jedna encja JPA. Klasa fasady dla tej encji to CustomerFacadeRest: package com.ensode.netbeansbook.jaxrs.service; import com.ensode.netbeansbook.jaxrs.Customer; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @Stateless @Path("com.ensode.netbeansbook.jaxrs.customer") public class CustomerFacadeREST extends AbstractFacade { @PersistenceContext(unitName = "jaxrsPU") private EntityManager em; @java.lang.Override protected EntityManager getEntityManager() { return em; } public CustomerFacadeREST() { super(Customer.class); } @POST @Override @Consumes({"application/xml", "application/json"}) public void create(Customer entity) { super.create(entity); } @PUT @Override @Consumes({"application/xml", "application/json"}) public void edit(Customer entity) { super.edit(entity); } @DELETE @Path("{id}") public void remove(@PathParam("id") Integer id) { super.remove(super.find(id)); } @GET
313
Java EE 6. Tworzenie aplikacji w NetBeans 7
@Path("{id}") @Produces({"application/xml", "application/json"}) public Customer find(@PathParam("id") Integer id) { return super.find(id); } @GET @Override @Produces({"application/xml", "application/json"}) public List findAll() { return super.findAll(); } @GET @Path("{from}/{to}") @Produces({"application/xml", "application/json"}) public List findRange(@PathParam("from") Integer from, @PathParam("to") Integer to) { return super.findRange(new int[]{from, to}); } @GET @Path("count") @Produces("text/plain") public String countREST() { return String.valueOf(super.count()); } }
Adnotacja @Stateless jednoznacznie wskazuje na to, że wygenerowana klasa to bezstanowe ziarno sesyjne. Adnotacja @Path służy do określenia identyfikatora URI, dla którego klasa ma obsługiwać żądania. Kilka metod klasy ma adnotacje @POST, @PUT, @DELETE i @GET. Metody te są automatycznie wywoływane, gdy usługa sieciowa odpowiada na odpowiednie żądania HTTP. Zauważ, że przy kilku metodach znajduje się też adnotacja @Path. Wynika to z tego, że niektóre metody wymagają parametru. Na przykład przy usuwaniu danych z tabeli CUSTOMER trzeba przekazać jako parametr klucz główny odpowiedniego wiersza. Składnia atrybutu value w adnotacji @Path to "{nazwa_zmiennej}". Tekst w nawiasach klamrowych to parametr określający ścieżkę. Warto zauważyć, że odpowiednie parametry w metodzie są opatrzone adnotacją @PathParam.
Testowanie usług sieciowych typu RESTful Po zainstalowaniu projektu można się upewnić, że usługa sieciowa działa poprawnie. W tym celu należy rozwinąć węzeł RESTful Web Services projektu, kliknąć prawym przyciskiem myszy usługę sieciową typu RESTful i wybrać opcję Test Resource Uri (rysunek 11.7).
314
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
Rysunek 11.7. Testowanie wygenerowanej usługi sieciowej
To spowoduje wywołanie metody findAll() usługi sieciowej (ponieważ jest to jedyna metoda, która nie wymaga parametrów) i automatyczne otwarcie w przeglądarce wygenerowanej odpowiedzi w formacie XML (rysunek 11.8).
Rysunek 11.8. Odpowiedź od testowanej usługi sieciowej
W tabeli CUSTOMER w używanej bazie danych znajduje się tylko jeden wiersz. XML-owa odpowiedź usługi sieciowej zawiera dane z tego wiersza.
315
Java EE 6. Tworzenie aplikacji w NetBeans 7
Można też łatwo przetestować inne metody danej usługi sieciowej. Wystarczy kliknąć projekt prawym przyciskiem myszy i wybrać opcję Test RESTful Web Services (rysunek 11.9).
Rysunek 11.9. Testowanie różnych metod usług sieciowych z projektu
Wtedy w przeglądarce automatycznie pojawi się strona podobna do tej z rysunku 11.10.
Rysunek 11.10. Strona do testowania metod usług sieciowych1
1
Uwaga — w niektórych przeglądarkach lewy panel jest niewidoczny. Można spróbować uruchomić test w innej przeglądarce lub skorzystać z dostępnej w najnowszych wersjach środowiska NetBeans samodzielnej aplikacji do przeprowadzania testów usług sieciowych — przyp. tłum.
316
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
Kliknij utworzoną usługę sieciową w lewym panelu, a następnie wybierz opcję GET(application/ xml) z listy rozwijanej Choose method to test i kliknij przycisk Test. Wtedy do usługi sieciowej typu RESTful przesłane zostanie żądanie HTTP GET, po czym usługa zwróci odpowiedź w formacie XML (rysunek 11.11).
Rysunek 11.11. Odpowiedź usługi sieciowej
Nie jest zaskoczeniem, że XML-owe dane są tu identyczne z danymi z poprzedniego przykładu. Utworzona usługa sieciowa typu RESTful może zwracać i przyjmować dane w formatach XML i JSON (ang. JavaScript Object Notation). Wskazują na to wartości w adnotacjach @Produces i @Consumes w kodzie. Aby wyniki zwracane przez metodę findAll() wyświetlić w formacie JSON, wystarczy wybrać opcję GET(application/json) i kliknąć przycisk Test (rysunek 11.12). Można też wstawiać, wczytywać i modyfikować pojedyncze rekordy. W tym celu należy wybrać właściwe żądanie HTTP i przekazać odpowiednie parametry. Spowoduje to automatyczne wywołanie potrzebnej metody z usługi sieciowej typu RESTful. Po stwierdzeniu, że utworzona usługa sieciowa typu RESTful została poprawnie zainstalowana, w następnym kroku należy zaimplementować aplikację kliencką korzystającą z tej usługi. Wcześniej jednak warto przyjrzeć się wygenerowanej przez środowisko NetBeans klasie Application Config. Aby ją wyświetlić, należy rozwinąć węzeł Generated Sources (rest) w okienku projektu (rysunek 11.13).
317
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 11.12. Usługa może też zwracać dane w formacie JSON
Rysunek 11.13. Plik z klasą ApplicationConfig
Oto kod źródłowy tej klasy: package org.netbeans.rest.application.config; /** * Klasa wygenerowana przez środowisko Netbeans IDE. * Rejestruje wszystkie podstawowe zasoby REST utworzone w projekcie. * Prosimy NIE edytować tej klasy! */ @javax.ws.rs.ApplicationPath("resources") public class ApplicationConfig extends javax.ws.rs.core.Application { }
318
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
Jak widać, ciało tej klasy jest puste. Służy ona do konfigurowania pracy interfejsu JAX-RS, dlatego nie trzeba w niej umieszczać kodu. Klasa ta musi tylko dziedziczyć po klasie javax.ws. rs.core.Application i mieć adnotację @javax.ws.rs.ApplicationPath. Adnotacja ta służy do określania początku identyfikatora URI wszystkich ścieżek określanych w adnotacjach @Path w klasach usług sieciowych typu RESTful. Środowisko NetBeans domyślnie dla wszystkich takich usług używa ścieżki resources.
Tworzenie klienta usług sieciowych typu RESTful Środowisko NetBeans udostępnia kreator, który automatycznie generuje kod klienta wywołujący metody usługi sieciowej typu RESTful za pomocą odpowiednich żądań HTTP. Aby wygenerować kod klienta, wystarczy wybrać opcję File/New File, otworzyć kategorię Web Services, a następnie kliknąć typ pliku RESTful Java Client (rysunek 11.14).
Rysunek 11.14. Tworzenie klienta usług sieciowych typu RESTful
Na następnej stronie kreatora należy wprowadzić nazwę klasy i pakietu klienta JAX-RS (rysunek 11.15).
319
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek 11.15. Konfigurowanie klienta
Trzeba też wybrać usługę sieciową typu RESTful, z której klient ma korzystać. Tu należy zaznaczyć przycisk opcji From Project w panelu Select the REST resource, a następnie kliknąć przycisk Browse… (rysunek 11.16).
Rysunek 11.16. Wybieranie usługi sieciowej dla klienta
Następnie wystarczy wybrać utworzoną wcześniej usługę sieciową typu RESTful. Wtedy środowisko NetBeans wygeneruje poniższy kod: package com.ensode.glassfishbook.jaxrsclient; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.UniformInterfaceException; import com.sun.jersey.api.client.WebResource;
320
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
/** Klient REST oparty na platformie Jersey wygenerowany dla zasobu REST CustomerFacadeREST [com.ensode.netbeansbook.jaxrs.customer]
* UŻYTKOWANIE: * NewJerseyClient client = new NewJerseyClient(); * Object response = client.XXX(…); * // Używanie odpowiedzi * client.close(); * * @author heffel */ public class NewJerseyClient { private WebResource webResource; private Client client; private static final String BASE_URI = "http://localhost:8080/jaxrs/resources"; public NewJerseyClient() { com.sun.jersey.api.client.config.ClientConfig config = new com.sun.jersey.api.client.config.DefaultClientConfig(); client = Client.create(config); webResource = client.resource(BASE_URI).path( "com.ensode.netbeansbook.jaxrs.customer"); } public void remove(String id) throws UniformInterfaceException { webResource.path(java.text.MessageFormat.format("{0}", new Object[]{id})).delete(); } public String countREST() throws UniformInterfaceException { WebResource resource = webResource; resource = resource.path("count"); return resource.accept( javax.ws.rs.core.MediaType.TEXT_PLAIN).get(String.class); } public T findAll_XML(Class responseType) throws UniformInterfaceException { WebResource resource = webResource; return resource.accept( javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType); } public T findAll_JSON(Class responseType) throws UniformInterfaceException { WebResource resource = webResource; return resource.accept( javax.ws.rs.core.MediaType.APPLICATION_JSON).get( responseType); } public void edit_XML(Object requestEntity) throws UniformInterfaceException { webResource.type(
321
Java EE 6. Tworzenie aplikacji w NetBeans 7
javax.ws.rs.core.MediaType.APPLICATION_XML). put(requestEntity); } public void edit_JSON(Object requestEntity) throws UniformInterfaceException { webResource.type( javax.ws.rs.core.MediaType.APPLICATION_JSON).put( requestEntity); } public void create_XML(Object requestEntity) throws UniformInterfaceException { webResource.type( javax.ws.rs.core.MediaType.APPLICATION_XML).post( requestEntity); } public void create_JSON(Object requestEntity) throws UniformInterfaceException { webResource.type( javax.ws.rs.core.MediaType.APPLICATION_JSON).post( requestEntity); } public T findRange_XML(Class responseType, String from, String to) throws UniformInterfaceException { WebResource resource = webResource; resource = resource.path( java.text.MessageFormat.format("{0}/{1}", new Object[]{from, to})); return resource.accept( javax.ws.rs.core.MediaType.APPLICATION_XML).get( responseType); } public T findRange_JSON(Class responseType, String from, String to) throws UniformInterfaceException { WebResource resource = webResource; resource = resource.path( java.text.MessageFormat.format("{0}/{1}", new Object[]{from, to})); return resource.accept( javax.ws.rs.core.MediaType.APPLICATION_JSON).get( responseType); } public T find_XML(Class responseType, String id) throws UniformInterfaceException { WebResource resource = webResource; resource = resource.path(java.text.MessageFormat.format("{0}", new Object[]{id})); return resource.accept( javax.ws.rs.core.MediaType.APPLICATION_XML).get( responseType);
322
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
} public T find_JSON(Class responseType, String id) throws UniformInterfaceException { WebResource resource = webResource; resource = resource.path(java.text.MessageFormat.format("{0}", new Object[]{id})); return resource.accept( javax.ws.rs.core.MediaType.APPLICATION_JSON).get( responseType); } public void close() { client.destroy(); } }
Jak widać, środowisko NetBeans generuje nakładki dla każdej metody z usługi sieciowej typu RESTful. Środowisko NetBeans generuje dwie wersje każdej metody. Jedna z nich produkuje i (lub) konsumuje dane w formacie XML, natomiast druga robi to samo dla danych w formacie JSON. Jak widać, w każdej metodzie używane są typy generyczne, co pozwala w czasie wykonywania programu określić typ wartości zwracanej przez te metody. Najłatwiejszym sposobem używania tych metod jest wykorzystanie łańcuchów znaków. Na przykład metodę find_XML(Class responseType, String id) można wywołać w następujący sposób: public class Main { public static void main(String[] args) { NewJerseyClient newJerseyClient = new NewJerseyClient(); String response = newJerseyClient.find_XML( String.class, "1"); System.out.println("Odpowiedź to: " + response); newJerseyClient.close(); } }
To wywołanie zwraca łańcuch znaków z XML-ową reprezentacją danych z wiersza o identyfikatorze 1 z bazy danych. Gdy wykonasz przedstawiony kod, powinny pojawić się następujące dane wyjściowe: Odpowiedź to: 1bnorris@ example.comBruceNorris
Następnie można w standardowy sposób przetwarzać kod w XML-u i manipulować nim. Ponadto można przesyłać XML-owe dane do usługi sieciowej. W tym celu wystarczy utworzyć łańcuch znaków z odpowiednim kodem w XML-u i przekazać go do jednej z wygenerowanych metod. Można na przykład wstawić wiersz do bazy danych za pomocą poniższego kodu:
323
Java EE 6. Tworzenie aplikacji w NetBeans 7
package com.ensode.glassfishbook.jaxrsclient; public class Main1 { public static void main(String[] args) { NewJerseyClient newJerseyClient = new NewJerseyClient(); String xml = "" + "" + "2" + "
[email protected]" + "Franciszek" + "Fajkowski" + "Fryderyk" + ""; newJerseyClient.create_XML(xml); newJerseyClient.close(); } }
Ten klient tworzy zrozumiały dla usługi sieciowej typu RESTful kod w formacie XML. Następnie kod ten należy przekazać do metody create_XML() z wygenerowanej klasy klienta. Klasa ta wywołuje usługę sieciową, a ta wstawia wiersz do bazy danych. Aby się przekonać, że dane zostały poprawnie wstawione, należy przesłać do bazy danych zapytanie z rysunku 11.17.
Rysunek 11.17. Sprawdzanie, czy dane zostały wstawione do bazy
Jak widać, dane w bazie pasują do danych z wygenerowanego łańcucha znaków w formacie XML.
324
Rozdział 11. • Usługi sieciowe RESTful oparte na interfejsie JAX-RS
Podsumowanie W tym rozdziale opisano wybrane z rozbudowanych możliwości środowiska NetBeans z zakresu generowania usług sieciowych typu RESTful. Dowiedziałeś się, że środowisko NetBeans umożliwia łatwe generowanie takich usług na podstawie schematu istniejącej bazy danych. Zobaczyłeś też, że można łatwo przetestować usługi sieciowe za pomocą narzędzi środowiska NetBeans i serwera GlassFish. Ponadto wyjaśniono, jak kilkoma kliknięciami myszą można wygenerować klienta usługi sieciowej. Dowiedziałeś się również, jak uruchamiać funkcje usługi sieciowej za pomocą kilku prostych wywołań metod.
325
Java EE 6. Tworzenie aplikacji w NetBeans 7
326
A Debugowanie aplikacji dla firm za pomocą debugera środowiska NetBeans Debugery pomagają testować i debugować aplikacje. Środowisko NetBeans udostępnia debuger, który pomaga w szybkim debugowaniu wszystkich aplikacji Javy — także aplikacji dla firm. W tym dodatku opisano debuger środowiska NetBeans z naciskiem na funkcje, które ułatwiają pracę programistom używającym Javy EE.
Debugowanie aplikacji dla firm Debugowanie aplikacji dla firm pisanych w Javie EE to zwykle skomplikowany proces. Serwer aplikacji musi wtedy działać w trybie debugowania. Procedura uruchamiania serwera w tym trybie zależy od samego serwera. Zwykle trzeba przekazać odpowiednie parametry wiersza poleceń do skryptu powłoki lub pliku wykonywalnego uruchamiającego dany serwer aplikacji. Ponadto kod trzeba skompilować z obsługą debugowania. Zazwyczaj trzeba w tym celu ustawić odpowiedni parametr w środowisku IDE lub przekazać potrzebne argumenty do pliku wykonywalnego javac. Trzeba też dołączyć debuger do serwera aplikacji, aby debuger miał dostęp do kodu uruchomionego w odrębnej maszynie JVM.
Java EE 6. Tworzenie aplikacji w NetBeans 7
Na szczęście jeśli używasz środowiska NetBeans i dołączonego do niego serwera aplikacji GlassFish, wszystkie kroki opisane w poprzednim akapicie są wykonywane automatycznie. Wystarczy wtedy otworzyć projekt typu Enterprise Application, kliknąć go prawym przyciskiem myszy i wybrać opcję Debug (rysunek A.1). Wtedy serwer aplikacji jest uruchamiany w trybie debugowania (lub zamykany i włączany w tym trybie, jeśli wcześniej działał w trybie standardowym), aplikacja zostaje zainstalowana i debuger jest automatycznie dołączany do serwera aplikacji.
Rysunek A.1. Debugowanie aplikacji typu Enterprise Application
Do zilustrowania możliwości środowiska NetBeans w zakresie debugowania posłuży tu prosta aplikacja korzystająca z technologii JSF, CDI i JPA. Przy utrwalaniu danych w encjach JPA trzeba albo jawnie określić klucz główny przy wstawianiu nowego wiersza do bazy, albo dodać adnotację @GeneratedValue, aby automatycznie wygenerować taki klucz. Przykładowa aplikacja nie korzysta z żadnej z tych technik, co prowadzi do błędu w momencie wstawiania nowych wierszy. Oto istotny tu kod encji JPA: package com.ensode.nbbook.buggywebapp.entitity; import java.io.Serializable; import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.xml.bind.annotation.XmlRootElement; @Entity @Table(name = "CUSTOMER") public class Customer implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false)
328
Dodatek A • Debugowanie aplikacji dla firm
@NotNull @Column(name = "CUSTOMER_ID") private Integer customerId; @Size(max = 20) @Column(name = "FIRST_NAME") private String firstName; @Size(max = 20) @Column(name = "MIDDLE_NAME") private String middleName; @Size(max = 20) @Column(name = "LAST_NAME") private String lastName; @Size(max = 30) @Column(name = "EMAIL") private String email; public Customer() { } public Customer(Integer customerId) { this.customerId = customerId; } // Ze względu na konieczność zachowania zwięzłości tekstu gettery i settery zostały pominięte }
Warto zauważyć, że w tej encji JPA nie ma adnotacji @GeneratedValue, dlatego przed utrwaleniem danych trzeba jawnie ustawić wartość klucza głównego. Poniższe ziarno nazwane CDI pełni w przykładowej aplikacji funkcję kontrolera. Zawiera metodę, która ma utrwalać dane z encji JPA Customer. package com.ensode.nbbook.buggywebapp.controller; // Operacje importu zostały pominięte @Named @RequestScoped @Stateful public class CustomerController { @PersistenceContext(unitName = "BuggyWebAppPU") private EntityManager em; @Inject private CustomerModel customerModel; public String createCustomer() { Customer customer = entityFromModel(customerModel); try { persist(customer); return "confirmation"; } catch (Exception e) { Logger.getLogger(getClass().getName()).log( Level.SEVERE, "przechwycono wyjątek", e); return "error"; }
329
Java EE 6. Tworzenie aplikacji w NetBeans 7
} public void persist(Object object) { try { em.persist(object); } catch (Exception e) { Logger.getLogger(getClass().getName()).log( Level.SEVERE, "przechwycono wyjątek", e); throw new RuntimeException(e); } } private Customer entityFromModel(CustomerModel customerModel) { Customer customer = new Customer(); customer.setFirstName(customerModel.getFirstName()); customer.setLastName(customerModel.getLastName()); return customer; } public CustomerModel getCustomerModel() { return customerModel; } public void setCustomerModel(CustomerModel customerModel) { this.customerModel = customerModel; } public EntityManager getEm() { return em; } public void setEm(EntityManager em) { this.em = em; } }
Zauważ, że powyższy kod nie ustawia właściwości klucza głównego w encji JPA Customer. Ponieważ nie zastosowano automatycznego generowania kluczy głównych i klucz nie jest jawnie ustawiany, aplikacja zgłasza wyjątek przy próbie utrwalenia egzemplarza encji JPA Customer. Gdy zajrzysz do dziennika serwera GlassFish w oknie na dane wyjściowe w środowisku NetBeans, znajdziesz następujący wiersz (rysunek A.2): at com.ensode.nbbook.buggywebapp.controller.CustomerController.createCustomer (CustomerController.java:35)
Rysunek A.2. Informacje o błędzie w środowisku NetBeans
330
Dodatek A • Debugowanie aplikacji dla firm
Wiersz ten informuje, że wyjątek wystąpił w wierszu 35. pliku CustomerController.java. Trzeba więc wstrzymać wykonywanie aplikacji tuż przed tym wierszem, aby sprawdzić wartości wszystkich istotnych zmiennych w tym momencie. Jedną z podstawowych cech debugerów jest możliwość wstrzymania wykonywania debugowanej aplikacji za pomocą punktów przerwania. Przed wykonaniem kodu z punktem przerwania aplikacja wstrzymuje pracę, co umożliwia sprawdzenie wartości wszystkich zmiennych egzemplarza i metody z klasy, w której umieszczono dany punkt. W środowisku NetBeans umieszczanie punktów przerwania obok wierszy jest bardzo łatwe — wystarczy w edytorze kodu źródłowego kliknąć lewy margines przy wierszu, do którego punkt przerwania ma zostać dodany. Wiersz jest wtedy wyróżniany czerwonym kolorem, a na lewym marginesie pojawia się czerwona kwadratowa ikona (rysunek A.3).
Rysunek A.3. Wiersz z punktem przerwania jest wyróżniony czerwonym kolorem i kwadratową ikoną na lewym marginesie Aby wyświetlić numery wierszy, kliknij lewy margines prawym przyciskiem myszy i zaznacz pole wyboru Show Line Numbers.
Teraz można przystąpić do debugowania aplikacji. W tym celu kliknij projekt prawym przyciskiem myszy i wybierz opcję Debug. Spowoduje to zainstalowanie aplikacji i uruchomienie jej w trybie debugowania. Należy przetestować aplikację w standardowy sposób, aby dojść do miejsca, w którym występuje błąd. W przykładowej aplikacji wystarczy wykonać kod do próby utrwalenia encji JPA. W edytorze widać, że wiersz z punktem przerwania jest teraz wyróżniony kolorem zielonym. Ponadto na lewym marginesie znajduje się strzałka (rysunek A.4).
331
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek A.4. Wykonywanie kodu doszło do punktu przerwania
W ten sposób wskazywany jest bieżący wiersz w ścieżce wykonania. Po wstrzymaniu pracy kodu można uruchomić go wiersz po wierszu, aby dokładnie wykryć, w czym tkwi problem. Każdy wiersz można wykonać na dwa sposoby. Można przeskoczyć wiersz albo wejść do niego. Różnica polega na tym, że przy przeskakiwaniu wiersza kod nie wchodzi do wywołań metod, tylko przechodzi przez wiersz. Wejście do wiersza powoduje przejście do uruchamianych w nim wywołań metod. W omawianym przykładzie przeskoczenie bieżącego wiersza powoduje pominięcie kodu, który utrwala dane. Dlatego odpowiednim podejściem jest tu wejście do bieżącego wiersza. W środowisku NetBeans można wejść do bieżącego wiersza za pomocą klawisza F7 lub ikony z rysunku A.5.
Rysunek A.5. Za pomocą tej ikony można wejść do bieżącego wiersza
Spowoduje to wykonanie metody z bieżącego wiersza, wstrzymanie pracy kodu, wyróżnienie zielonym kolorem wiersza przeznaczonego do wykonania w następnym kroku i umieszczenie strzałki na lewym marginesie obok tego wiersza (rysunek A.6). Ponieważ wykonywana metoda zawiera tylko jeden wiersz wykonywalny, wiadomo, że przeskoczenie go spowoduje wyjątek. Wartości zmiennych lokalnych z bieżącej klasy i metody można sprawdzić w oknie Local Variables (rysunek A.7). Aby otworzyć okno Local Variables, wybierz opcję Window/Debugging/Local Variables lub wciśnij kombinację Alt+Shift+1 (w systemach Windows i Linux) albo Ctrl+Shift+1 (w systemach z rodziny Mac OS X).
332
Dodatek A • Debugowanie aplikacji dla firm
Rysunek A.6. Bieżący i następny wiersz są wyróżnione kolorami
Rysunek A.7. W oknie Local Variables widoczne są zmienne lokalne
Gdy rozwiniesz węzeł odpowiadający obiektowi customer, zobaczysz wartości wszystkich jego właściwości. Powinieneś zauważyć, że właściwość customerId ma wartość null, co oznacza, że kod próbuje wstawić nowy wiersz bez klucza głównego. W ten sposób błąd został wykryty. Gdy znasz już problem, rozwiązanie go jest bardzo proste. Najłatwiej dodać adnotację @Generated Value do właściwości customerId ziarna encyjnego.
Podsumowanie W tym dodatku opisano, jak debugować aplikacje dla firm za pomocą środowiska NetBeans. Dowiedziałeś się, że dzięki środowisku NetBeans debugowanie zdalnych aplikacji zainstalowanych na serwerze aplikacji jest równie proste jak debugowanie standardowych aplikacji Javy. Dowiedziałeś się też, jak dodawać punkty przerwania. Punkty te powodują wstrzymanie pracy aplikacji, po czym można wejść do wybranych wierszy lub je przeskoczyć. Zobaczyłeś też, jak za pomocą okna Local Variables sprawdzić wartości wszystkich dostępnych zmiennych. 333
Java EE 6. Tworzenie aplikacji w NetBeans 7
334
B Wykrywanie problemów z wydajnością za pomocą profilera środowiska NetBeans Czasem w aplikacjach występują problemy z wydajnością. Zdarza się, że wykrycie kodu, który wymaga optymalizacji, jest bardzo proste, jednak nieraz wymaga to sporo pracy. Profilery to narzędzia, które pomagają w wykryciu problemów z wydajnością w kodzie. Środowisko NetBeans ma bardzo dobry profiler, który można wykorzystać dla aplikacji Javy EE.
Profilowanie aplikacji Aby przeprowadzić profilowanie aplikacji, wystarczy kliknąć ją prawym przyciskiem myszy w oknie Projects i wybrać opcję Profile. Pojawi się wtedy okno dialogowe z rysunku B.1. Jak widać, profilowanie może dotyczyć kilku aspektów aplikacji (np. przydziału pamięci i poziomu wykorzystania procesora). Jedną z najbardziej przydatnych funkcji profilera w środowisku NetBeans jest informowanie, jak długo trwało wykonywanie poszczególnych metod aplikacji. Dane te są dostępne przy profilowaniu poziomu wykorzystania procesora. Aby rozpocząć profilowanie, kliknij przycisk Run. Spowoduje to włączenie serwera aplikacji w trybie profilowania oraz zainstalowanie i uruchomienie aplikacji. Po kilku sekundach pojawi się panel sterowania profilera (rysunek B.2).
Java EE 6. Tworzenie aplikacji w NetBeans 7
Rysunek B.1. Okno dialogowe do profilowania aplikacji
Rysunek B.2. Panel sterowania profilera
Teraz można ustalić, jak długo trwa wykonywanie każdej metody i ile razy każda z nich jest uruchamiana (rysunek B.3). W tym celu wystarczy kliknąć przycisk Live Results. W oknie Live Profiling Results uwzględnione jest każde wywołanie metody. Możesz się dowiedzieć, przez ile procent czasu pracy aplikacji działa każda metoda, ile milisekund zajmuje wykonanie metod, a także ile razy poszczególne metody były wywoływane. Jak widać, profiler środowiska NetBeans jest bardzo przydatny przy wykrywaniu niewydajnych obszarów aplikacji. Pozwala to łatwo zidentyfikować miejsca, na których należy skoncentrować prace optymalizacyjne.
336
Dodatek B • Wykrywanie problemów z wydajnością
Rysunek B.3. Szczegółowe informacje o czasie wykonywania poszczególnych metod
Choć podane informacje są bardzo cenne, nie są to jedyne dane dostępne w profilerze środowiska NetBeans. Może też sprawdzić, ile pamięci zajmuje aplikacja. W tym celu należy kliknąć ikonę VM Telemetry na panelu sterowania profilera (rysunek B.4), aby otworzyć okno VM Telemetry.
Rysunek B.4. Kliknij tę ikonę, aby wyświetlić okno VM Telemetry
Gdy klikniesz tę ikonę, pojawi się okno VM Telemetry (rysunek B.5). Zawiera ono trzy zakładki.
Rysunek B.5. Okno VM Telemetry
Wykres z pierwszej od lewej zakładki (kolor czerwony) określa łączną ilość pamięci na stercie przydzielonej w maszynie JVM aplikacji. Ponadto podana jest łączna ilość pamięci na stercie zajęta przez aplikację.
337
Java EE 6. Tworzenie aplikacji w NetBeans 7
Wykres ze środkowej zakładki przydaje się do wykrywania wycieków pamięci (rysunek B.6). Java ma mechanizm przywracania pamięci, dlatego teoretycznie wyciek pamięci jest niemożliwy. Jeśli jednak zachowasz referencje do nieużywanych już obiektów, pamięć zajmowana przez te obiekty nie zostanie odzyskana, dlatego może nastąpić wyciek pamięci. Fioletowa linia na tym wykresie określa czas poświęcany na przywracanie pamięci w maszynie JVM. Czerwona linia oznacza nieusunięte generacje (ang. surviving generations). Generacja to zbiór obiektów utworzonych w czasie dwóch przebiegów przywracania pamięci. Nieusunięta generacja to taka, która nie została usunięta w trakcie jednego lub więcej przebiegów przywracania pamięci. Można wymusić na aplikacji przywrócenie pamięci za pomocą przedstawionej na rysunku B.7 ikony z panelu sterowania profilera.
Rysunek B.6. Środkowa zakładka zawiera informacje o przywracaniu pamięci
Rysunek B.7. Za pomocą tej ikony można wymusić przywrócenie pamięci w aplikacji
Jeśli wykres informuje o dużej liczbie nieusuniętych generacji w okresie między przebiegami przywracania pamięci, może to wskazywać na wyciekanie pamięci (rysunek B.8).
Rysunek B.8. Duża liczba nieusuniętych generacji może oznaczać wyciekanie pamięci
338
Dodatek B • Wykrywanie problemów z wydajnością
Wykres z prawej zakładki okna VM Telemetry określa liczbę aktywnych wątków, a także liczbę klas wczytanych w aplikacji.
Podsumowanie Profiler środowiska NetBeans to bardzo przydatne narzędzie do identyfikowania problemów z wydajnością w aplikacji. Zapewnia kilka przydatnych mechanizmów, które pomagają wykrywać niewydajny kod, a także łatwo śledzić ilość zajmowanej pamięci i liczbę obiektów w aplikacji. Więcej informacji na temat profilera środowiska NetBeans znajdziesz pod adresem http:// profiler.netbeans.org/.
339
Java EE 6. Tworzenie aplikacji w NetBeans 7
340
Skorowidz A adnotacja @ ActivationConfigProperty, 283 AroundInvoke, 240 EJB, 301 Entity, 193, 206 FacesValidator, 151 Id, 193 InterceptorBinding, 263 Interceptors, 240, 241 ManagedBean, 145 MessageDriven, 283 Named, 253 NamedQueries, 207 Path, 314, 319 Qualifier, 256 RequestScoped, 145 Schedule, 243 Stateless, 314 Table, 207 TransactionAttribute, 238 ViewScoped, 145 WebServlet, 73 adres URL, 77 aktualizowanie danych w bazie, 116 AOP, Aspect Oriented Programming, 248 aplikacje CDI, 251 debugowanie, 327 JSF, 132, 217, 251 JSP, 215 profilowanie, 335 sieciowe, 51, 131 z kwalifikatorami, 260
architektura JMS, 270 atrybuty dyrektywy strony, 57 automatyczna obsługa zasobów, 159 automatyczne generowanie encji, 200 klas, 296 komunikatów, 136, 149 stron, 167
B baza danych, 308 aktualizowanie danych, 116 modyfikowanie danych, 114 schemat APP, 190 usuwanie danych, 119 wstawianie danych, 114 bezpieczeństwo aplikacji, 86 serwera, 89 bezstanowe ziarno sesyjne, 298, 314 biblioteka JSTL, 97, 122, 126 PrimeFaces, 165 znaczników niestandardowych, 127
C CDDL, Common Development and Distribution License, 21 CDI, Contexts and Dependency Injection, 249
Skorowidz
D DAO, Data Access Object, 195 debuger, 327–333 deskryptor glassfish-web.xml, 90 wdrażania, 73, 85 docelowa lokalizacja JMS, 272 dodawanie adnotacji @Interceptors, 241 deskryptora wdrażania, 85, 108 grup bezpieczeństwa, 90 instrukcji INSERT, 114 metod do ziaren, 232, 248 nowej strony JSP, 67 pliku klienta, 294 pól, 62 projektu biblioteki, 234 referencji, 109 ról zabezpieczeń, 86 serwletu, 72 szablonu faceletu, 154 utrwalanych pól, 194 użytkowników, 92 znaczników, 113 dokument JSR, 208 domena bezpieczeństwa, security realm, 81 dostęp do obiektów JSTL, 104 stron, 87 ziarna, 233 dostępne metody obiektu, 70 obiekty, 69 dyrektywa strony JSP, 56 działanie typu do wiązania interceptorów, 266 dzielenie formularzy, 178
E EAR, Enterprise Application, 224 EJB, Enterprise JavaBeans, 223 ekran instalatora, 25 startowy, 26 encje JPA, 186, 192, 215, 244 etap projektu JSF, 135
342
F fabryka połączeń, 274 facelet, 136, 251 format JSON, 317 XML, 317 formatowanie kodu, 60 formularz, 58, 65 fragmenty JSP, 93
G generowanie aplikacji JSP, 215 encji JPA, 200, 203, 205 interfejsów, 178 klas, 296, 311 klasy kontrolerów, 196 kluczy głównych, 193, 218 kodu do wysyłania komunikatów, 276 klienta, 296, 319 kwalifikatora, 256 nowego typu, 264 stereotypu, 262 usługi sieciowej, 287, 299, 302, 305 kwerend JPQL, 207 metod, 291 serwletów, 73 stron, 167 stron JSF, 215 szablonów, 152 szablonów faceletu, 170 tabeli, 61 usług sieciowych, 308 ziaren sesyjnych, 231, 244 getter, 145, 195 GPL, GNU Public License, 21 grupa bezpieczeństwa, 90
H hierarchia bieżącej klasy, 45
Skorowidz
I IDE, Integrated Development Environment, 15 identyfikator JNDI, 224, 276 URI, 98, 307 ikona błąd kompilacji, 48 implementacja interfejsu, 49 ostrzeżenie, 48 przesłanianie metod, 48 implementowanie klienta, 317 strony błędu logowania, 84 uwierzytelniania, 82 warstwy biznesowej, 223 informacje o bazie danych, 201 o przywracaniu pamięci, 338 instalowanie aplikacji, 35 instalowanie środowiska, 19 Linux, 20 Mac OS X, 19 Microsoft Windows, 19 Solaris, 20 instrukcja DELETE, 120, 121 INSERT, 114 UPDATE, 119 integrowanie środowiska, 27 z serwerem aplikacji, 27 z systemem RDBMS, 30 interceptor, 239, 263 interfejs API Servlet, 51 CDI, 249 graficzny, 288 javax.jms.Message, 278 javax.jms.MessageListener, 283 javax.servlet.jsp.jstl.sql.Result, 110 JAX-RS, 307 JAX-WS, 285 JMS, 270 JPA, 185 JTA, 192 lokalny, 230 zdalny, 230
J J2EE, 223, 237 Java EE, 16 Java ME, 16 Java SE, 16 JAXB, Java API for XML Binding, 203 JAX-RS, 307 JDK, Java Development Kit, 19 język JPQL, 207 UEL, 252 JMS, Java Messaging Service, 269 JNDI, Java Naming and Directory Interface, 109 JPA, Java Persistence API, 16, 185 JPQL, Java Persistence Query Language, 207 JSF, JavaServer Faces, 54, 131 biblioteka PrimeFaces, 165 komponenty złożone, 159 nowy projekt, 133 poprawność danych, 148 szablony, 152 JSON, JavaScript Object Notation, 317 JSP, Java Server Pages, 51 JSR, Java Specification Request, 208 JSTL, Java Standard Tag Library, 97 JTA, Java Transaction API, 192
K kalendarz, 171 katalog instalacyjny, 23 resources, 140 klasa AbstractClass, 312 ApplicationConfig, 318 interceptora, 240 ValidatorException, 151 klasy fasady, 311 kontrolerów, 195 klient, 237 szablonu, 158 usługi sieciowej, 293, 319 klucz główny, 193 kod klienta usługi sieciowej, 297
343
Skorowidz
kolejka komunikatów, 272 komponent kreatora, 181 komponenty PrimeFaces, 165, 183 złożone, 159 komunikacja z bazą, 187 komunikat JMS, 271, 276 o błędzie, 149, 151, 171 SOAP, 289 konfigurowanie bezpieczeństwa aplikacji, 86, 89, 90 dostępu do stron, 87 fabryki połączeń, 275 klasy kontrolera, 197 klienta, 320 kodu do wysyłania komunikatów, 277 kolejki komunikatów, 273 komponentu, 160 kwalifikatora, 257 metody, 232 nowego ziarna, 281 odwzorowań, 205 operacji, 289 operacji wstawiania danych, 114 połączenia, 190 procesu generowania ziaren, 245 serwletu, 74 środowiska, 27 usługi sieciowej, 301, 304 ziarna, 144 ziarna sesyjnego, 229 znacznika , 102 , 105 , 99 , 111 , 117, 120 konsola administracyjna serwera, 90, 91 kontener serwletów, 22 kontroler, 72 kreator Create Persistence Unit, 187 interfejsu, 180 klas encji, 187 New Entity Classes from Database, 207 New JSF Managed Bean, 145 usług sieciowych, 299
344
kwalifikator CDI, 256 kwerendy nazwane, 207
L licencja CDDL, 21 GPL, 21 lista obiektów, 69 szablonów kodu, 44
Ł łańcuch znaków, 323
M mechanizm MTOM, 290 metoda GET, 59 getProjectStage(), 136 getServletContext(), 77 getSession(), 77 main(), 278 POST, 59 saveCustomer(), 181 sendJMSMessageToMyQueue(), 278 metody interfejsu javax.servlet.jsp.jstl.sql.Result, 110 model, 72 modyfikowanie danych w bazie, 114 deskryptora wdrażania, 89 kodu, 57 skryptletu, 79 strony, 58, 138 usługi sieciowej, 288 MVC, Model-View-Controller, 72
N narzędzia diagnostyczne, 136 nawigacja dynamiczna, 142 statyczna, 142
Skorowidz
nazwa grupy, 90 nazwy ról zabezpieczeń, 90 NetBeans instalowanie aplikacji, 35 instalowanie środowiska, 19 konfigurowanie środowiska, 27 pierwsze uruchomienie, 26 pobieranie środowiska, 16 wydajne programowanie, 38 nowa baza danych, 201 encja, 216 operacja, 289 pula połączeń, 189 rola, 87 strona, 66 usługa sieciowa, 300 nowe operacje usługi, 290 ziarno, 281 ziarno sesyjne, 229 nowy deskryptor wdrażania, 109 egzemplarz encji, 219 fragment JSP, 95 projekt, 36 projekt JSF, 133 szablon faceletu, 153
O obiekty DAO, 195 na stronach JSP, 69 POJO, 186, 298 SurveyData, 76 obsługa interfejsu CDI, 250 widoków, 251, 253 okno Local Variables, 333 usługi sieciowej, 302 VM Telemetry, 337 określanie pakietu zasobów, 310 opcja Connect Using…, 33 Create Database…, 200
Deploy, 101 Getters and Setters, 145 Run File, 64 Test Resource Uri, 314 operacje CRUD, 307 usługi sieciowej, 295 operatory JSTL, 104
P pakiet JDK, 19 pakiety środowiska NetBeans, 18 platforma JSF, 54, 131 plik glassfish-resources.xml, 276 web.xml, 90, 108, 135 pliki EAR, 224 JSP, 56, 68 WAR, 224 WSDL, 286, 293, 303 znaczników niestandardowych, 122 pobieranie danych, 138 danych z bazy, 109 referencji do obiektu, 235 środowiska, 16 POJO, Plain Old Java Object, 186, 298 pola do wprowadzania danych, 62 pole wyboru, 62, 63 połączenie z RDBMS, 32 profiler, 335–339 programowanie aspektowe, 239, 248, 263, 267 serwletów, 72 projekt Enterprise Application, 225, 271 Java Class Library, 227 protokół REST, 285 SOAP, 285 przenoszenie wspólnego kodu, 96 przesyłanie danych, 323 formularza, 64 komunikatów, 275–279
345
Skorowidz
przycisk Remove Operation, 289 pula połączeń, 187, 188
R referencja do interfejsu zdalnego, 236 relacja jeden do jednego, 211, 213 jeden do wielu, 211, 213 wiele do jednego, 211 wiele do wielu, 211, 213 relacje dwukierunkowe, 214 jednokierunkowe, 214 między encjami, 209 REST, Representational State Transfer, 285, 307 role zabezpieczeń, 86
S schemat APP, 190 bazy danych, 34, 203 serwer aplikacji, 19, 27 aplikacji GlassFish, 22, 54 Tomcat, 54 serwlet, 51, 72, 74 setter, 145, 195 siatka, 139 skrót, 82 skróty klawiaturowe, 43 Alt+Enter, 46 Alt+F12, 45 Alt+F7, 46 Alt+Insert, 45, 145 Ctrl+[, 45 Ctrl+E, 46 Ctrl+F12, 43 Ctrl+Shift+[, 45 Ctrl+Shift+I, 47 Ctrl+Shift+N, 52 Ctrl+Spacja, 38, 57 Shift+Alt+F, 46, 60 Shift+Alt+O, 46 Shift+F6, 64
346
skrypt create_populate_tables.sql, 202 skryptlety JSP, 71 SOAP, Simple Object Access Protocol, 285 sprawdzanie poprawności danych, 148 ziaren, 208 SQL-owe znaczniki, 109 stereotyp CDI, 260 sterownik JDBC, 31, 189 stosowanie kwalifikatorów, 258 strona błędu logowania, 84 confirmation.xhtml, 255 do wprowadzania danych, 57 domyślna, 38 logowania, 92 startowa aplikacji, 218 wyświetlająca dane, 66 z potwierdzeniem, 146, 172, 260 strony JSP, 56 system JavaDB, 189, 190 RDBMS, 27, 31, 189 szablony, 152, 154 szablony kodu, 41, 42 szkielet projektu JSF, 135
Ś ścieżka resources, 319
T technologia JSP, 51 testowanie usługi sieciowej, 291, 314–317 Tomcat, 22 transakcje, 238 tryb potwierdzania, 279, 283 tworzenie aplikacji, 166 aplikacji JSF, 132, 169 aplikacji sieciowych, 51, 131 bazy danych, 201 deskryptora wdrażania, 89 docelowej lokalizacji JMS, 272 encji JPA, 186
Skorowidz
fragmentu JSP, 93, 94 klas kontrolerów, 195 klasy encji, 186 klasy interceptora, 240 klienta szablonu, 155 klienta usług sieciowych, 293, 295, 319 kolejki komunikatów, 273 komponentów złożonych, 160 kwalifikatora, 256 obiektów DAO, 195 pliku ziarna, 280 pliku znacznika JSP, 122 połączenia JMS, 279 projektu Enterprise Application, 225 projektu Java Class Library, 227 puli połączeń, 189 serwletów, 72 stereotypu, 261 strony wyświetlającej dane, 66 strony z potwierdzeniem, 146 szablonów faceletów, 152 usług sieciowych, 285, 298, 303, 308 ziarna, 76 ziarna sesyjnego, 224, 228 ziarna zarządzanego, 143 źródła danych, 189 typy do wiązania interceptorów, 263
U udostępnianie obiektu POJO, 298 ziaren EJB, 298, 301 UEL, Unified Expression Language, 252 URI, Uniform Resource Identifier, 307 URI, Unique Resource Identifier, 98 uruchamianie aplikacji, 147 klienta, 237 projektu, 55 środowiska, 26 usługi sieciowe RESTful, 307, 311 SOAP, 285 usługi zegara, 243
ustawianie pakietu, 309 właściwości pola wyboru, 63 właściwości przycisku, 64 właściwości serwletu, 73 właściwości strony JSP, 67 usuwanie danych z bazy, 119 operacji usługi, 289 utrwalanie danych, 191, 194 uwierzytelnianie użytkownika certyfikat po stronie klienta, 82 oparte na formularzu, 82–85 podstawowe, 81 z wykorzystaniem skrótu, 82 uzupełnianie kodu, 38, 40 nazw, 38, 41 używanie szablonów, 154
W walidator niestandardowy, 150 wartości adnotacji @TransactionAttribute, 238 wiązanie interceptorów, 263, 265 widok, 72 właściwości kolejki komunikatów, 274 pola wyboru, 63 przycisku, 64 serwletu, 73 strony JSP, 67 wprowadzanie danych, 57 wskazówki wizualne, 47 wstawianie formularzy, 59 tabeli, 60 wybieranie encji, 216, 245 klasy encji, 196 tabel, 204, 309 usługi dla klienta, 320 wyciek pamięci, 338 wydajne programowanie, 38, 335 wyjątki JMSException, 282 ValidatorException, 151
347
Skorowidz
wysyłanie komunikatów, 276 wyświetlanie części strony, 98 stron, 88 wzorzec projektowy DAO, 195 Fasada, 311 MVC, 72
Z zabezpieczanie aplikacji sieciowych, 81 zakładka Adres, 177 zakładki, 173, 176 załącznik komunikatu, 290 zarządzanie transakcjami, 238 zasięg ziarna, 79, 254 zasób REST, 310 zegar, 243 ziarno CDI, 252 EJB, 223, 247, 298 encyjne, 185, 223 JavaBeans, 76 sesyjne, 223, 228 bezstanowe, 224 stanowe, 224 sterowane komunikatami, 242, 271, 280, 282 zarządzane, 143, 145 addressBean, 163 CustomerController, 180 zintegrowane środowisko programistyczne, IDE, 15 zmienna środowiskowa JAVA_HOME, 22 znacznik , 98, 101 , 98, 104 , 98 , 162 , 162
348
, 265 , 151 , 142 , 138, 178 , 141 , 141 , 141 , 141 , 139 , 142 , 141 , 265 , 171 , 167 , 167 , 177 , 170 , 173 , 178 , 116 , 109 , 114, 117 , 156 , 156 , 156 znaczniki JSTL, 98 niestandardowe JSP, 122 PrimeFaces, 167 SQL-owe, 107
Ź źródło danych, 111, 188
Ż żądanie GET, 78 żądanie POST, 78