Rzeczywistość 297
Niniejszy rozdział zawiera informacje dotyczące sposobów zastosowania języka XML w danych służących do konfiguracji. W poprzednich rozdziałach Czytelnik poznawał sposoby zastosowania tego języka do przesyłania danych pomiędzy aplikacjami i do tworzenia warstwy prezentacyjnej — w tym rozdziale zostaną omówione sposoby wykorzystania XML-a do przechowywania danych. Aby zrozumieć przesłanki przemawiające za użyciem XML-a na potrzeby konfiguracji, wystarczy napisać aplikację korzystającą z obszernych plików właściwości albo serwer konfigurowany na podstawie plików, a nie argumentów wiersza poleceń. W obu tych przypadkach format plików dostarczających informacje do aplikacji jest umowny i zazwyczaj odpowiada tylko określonej aplikacji. Programista ustala format pliku, buduje moduł odczytujący taki plik i aplikacja zostaje związana ze swoim plikiem konfiguracyjnym na zawsze. Oczywiście, takie postępowanie nie uwzględnia dalekowzrocznych planów związanych z rozwijaniem tej aplikacji.
Programiści i inżynierowie systemów zdali sobie sprawę z kłopotów z pielęgnacją tak napisanego oprogramowania (zapominanie o przecinkach, niepewność dotycząca znaków wskazujących początek komentarza itd.). Jasne stało się, że potrzebny jest standard reprezentacji tego typu danych niezwiązany z konkretną aplikacją. Jednym z takich standardów, wciąż wykorzystywanym, ale pozbawionym wielu użytecznych cech, jest klasa java.util.Properties i odpowiednie pliki właściwości. Konstrukcje te, wprowadzone w pakiecie Java Development Kit (JDK) 1.0, umożliwiają wygodne z punktu widzenia Javy przechowywanie informacji konfiguracyjnych, jednakże nie udostępniają żadnego sposobu grupowania lub budowania hierarchii. Aplikacja klienta ma taki dostęp do informacji serwera, jak serwer do informacji klienta i programista nie jest w stanie przeprowadzić żadnego logicznego grupowania wewnątrz takiego pliku. Co więcej, kiedy hierarchiczne parametry konfiguracyjne zyskały na popularności, takie zagnieżdżanie było trudne do osiągnięcia za pomocą innych rozwiązań bez wprowadzania jeszcze bardziej złożonych (a wciąż współpracujących tylko z daną aplikacją) formatów plików. XML przyniósł rozwiązanie wszystkich tych problemów — oferował standardowy, prosty sposób reprezentacji informacji konfiguracyjnych.
Format XML nadaje się do szerokich zastosowań administracyjnych. Możliwe jest zbudowanie ogólnej aplikacji ładującej definicję DTD lub schemat, a następnie plik konfiguracyjny i umożliwiającej dodawanie, aktualizację, usuwanie i modyfikację informacji konfiguracyjnych. Jedna aplikacja, wykorzystująca jeden lub wiele plików konfiguracyjnych XML, mogłaby służyć jako jeden interfejs administracyjny. Jeśli porównamy to z całą mnogością plików haseł, plików shadow, użytkowników, grup i skryptów inicjalizacyjnych, to na pewno takie rozwiązanie jest przejrzystsze i prostsze w obsłudze.
Ponieważ XML jest już wykorzystywany w wielu aplikacjach, dodanie rozszerzenia do przetwarzania i obsługi plików konfiguracyjnych było tylko kwestią czasu. Aplikacje nie wykorzystujące jeszcze XML-a mogą wprowadzać pliki konfiguracyjne oparte na tym języku; jest to o wiele prostsze niż dodanie obsługi przesyłania danych XML lub przekształcania XML-a do innych formatów. Tak czy inaczej, konfiguracja za pomocą XML-a okazała się dobrym rozwiązaniem w wielu sytuacjach. Od kiedy przedstawiono specyfikację Enterprise JavaBeans(EJB) 1.1, wymagającą deskryptorów wdrożeniowych EJB w formacie XML, wykorzystanie tego języka w konfiguracji stało się bardzo popularne. Wielu programistów obawiających się obciążenia wynikającego z uruchomienia parsera XML lub wątpiących w perspektywy standardu XML nagle musiało wykorzystać ten język do wdrożenia obiektów biznesowych w serwerach EJB. Spowodowało to logiczną migrację wszystkich danych konfiguracyjnych do tego formatu i przyczyniło się nawet do zmniejszenia złożoności niejednej aplikacji. W niniejszym rozdziale zostaną przedstawione sposoby wykorzystania XML-a do konfigurowania własnej aplikacji.
Najpierw zostaną omówione używane obecnie sposoby korzystania z XML-a na potrzeby konfiguracji. Przedstawione zostaną deskryptory wdrożeniowe EJB pod kątem decyzji projektowych, jakie podjęto przy tworzeniu specyfikacji tego rodzaju plików. To stanowić będzie przygotowanie do stworzenia własnych plików konfiguracyjnych XML. Zaczniemy od zbudowania pliku konfiguracyjnego dla serwera, który stworzyliśmy w poprzednim rozdziale. Następnie napiszemy klasy narzędziowe przetwarzające i ładujące te informacje do naszych klas XML-RPC — to znacznie zwiększy elastyczność omawianego serwera i jego klientów. Informacje o konfiguracji w prosty sposób załadujemy za pomocą interfejsów JDOM. W zakończeniu rozdziału zostaną porównane możliwości XML-a i innych mechanizmów składowania danych — baz danych i serwerów usług katalogowych. Dzięki temu Czytelnik będzie mógł wyrobić sobie opinię na temat praktycznych zastosowań XML-a.
Zanim zbudujemy własne pliki konfiguracyjne oraz oprogramowanie pozwalające z nich korzystać, przyjrzymy się istniejącym formatom i szablonom. Tematem niniejszej książki nie jest specyfikacja EJB, ale krótki opis deskryptorów wdrożeniowych EJB potrzebny jest do zrozumienia zasady działania plików konfiguracyjnych XML; opis ten podpowie również, jak zaprojektować własny format danych konfiguracyjnych. W niniejszym podrozdziale zostaną także omówione najważniejsze decyzje projektowe leżące u podstaw plików konfiguracyjnych deskryptorów.
Jednak przed omówieniem deskryptorów wdrożeniowych warto zastanowić się, dlaczego w ogóle programiści EJB „przeszli” na XML. Specyfikacja EJB 1.0 wymagała stosowania uszeregowanych deskryptorów wdrożeniowych; niestety, to była jedyna wskazówka oferowana przez autorów specyfikacji. Każdy producent EJB wymagał więc własnego formatu deskryptorów wdrożeniowych dla swojego serwera i zmuszał programistę do uruchamiania narzędzia (a nawet pisania własnych narzędzi) szeregujących deskryptor. Specyfikacja EJB (a także ogólnie język Java) utraciła prawo do miana WORA (Write Once Run Anywhere — aplikacja raz napisana może zostać uruchomiona gdziekolwiek). XML udostępniał standardowy sposób obsługi deskryptorów wdrożeniowych, a także zniwelował potrzebę korzystania z własnych narzędzi szeregujących. Co więcej, firma Sun udostępniła definicję DTD EJB, dzięki której deskryptory wdrożeniowe dowolnego producenta zgodne są z jedną specyfikacją; system EJB został w dużym stopniu uniezależniony od platformy i produktów określonego producenta.
Podobnie jak w przypadku dowolnego dokumentu XML, deskryptor wdrożeniowy EJB posiada definicję DTD, z którą utrzymuje zgodność. Jak już wcześniej wspomniano, w przyszłych wersjach specyfikacji definicja ta zostanie prawdopodobnie zastąpiona schematem. Tak czy inaczej, dokumenty XML używane w konfiguracji muszą tutaj (nawet bardziej niż gdzie indziej) przestrzegać narzuconych na nie reguł. Bez zawężeń informacje w nich zamieszczone mogłyby okazać się niepoprawne lub bezużyteczne, a na tym cierpiałaby cała aplikacja. Po określeniu zawężeń deskryptor wdrożeniowy rozpoczyna się od elementu głównego ejb-jar. Niniejsza uwaga może wydawać się banalna, ale nazwanie elementu głównego stanowi istotny element budowania dowolnego dokumentu XML. Na elemencie tym spoczywa ciężar odpowiedzialności za reprezentację wszystkich informacji zawartych w danym dokumencie XML. Szczególnie w przypadku, kiedy inne osoby będą musiały zarządzać naszymi dokumentami, właściwe ich nazwanie może oszczędzić im wiele kłopotów. Poniżej pokazano istotne fragmenty deskryptora wdrożeniowego XML zgodnego ze specyfikacją EJB:
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise
JavaBeans 1.1//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<description>
Ten plik ejb-jar zawiera obiekty enterprise beans będące
częścią aplikacji samoobsługowej dla pracowników.
</description>
...
</ejb-jar>
Zastosowanie przestrzeni nazw (które w czasie tworzenia specyfikacji EJB 1.1 były jeszcze w powijakach) powoduje, że nazewnictwo elementu głównego i pozostałych jest bardziej przejrzyste. To zaś upraszcza określenie celu dokumentu; należy zauważyć, jak wiele dwuznaczności uniknięto poprzez zastosowanie w deskryptorze wdrożeniowym przestrzeni nazwy takiej jak DeploymentDescriptor lub nawet EJB-DD. Wystarczy spojrzeć na przedrostek elementu, a wszystkie wątpliwości znikają.
Podobnie jak nazwy są istotne dla przejrzystości dokumentu, jego organizacja ma zasadnicze znaczenie dla użyteczności danych jako źródła konfiguracji. Sposób organizacji i zagnieżdżania elementów i atrybutów nie tylko pomaga w zrozumieniu przeznaczenia dokumentu, ale gwarantuje również, że zawarte w nim informacje konfiguracyjne będą mogły być wykorzystane w różnych aplikacjach. Równie ważna jest umiejętność ustalenia, kiedy nie składować informacji tak, by była ona współużytkowana. Szczególnie istotny jest tutaj przypadek deskryptora wdrożeniowego; każdy obiekt EJB ma działać niezależnie od innych i posiadać tylko te informacje, które uzyskał z odpowiadającego mu pojemnika (ang. containter). Może to stanowić problem, jeśli obiekty bean współdziałają ze sobą poza ścisłymi zasadami narzucanymi przez programistę — istnieje wtedy możliwość zburzenia logiki biznesowej i obniżenia wydajności. Dlatego właśnie każdy wpis EJB jest niezależny od pozostałych.
W poniższym przykładzie za pomocą dokumentu XML opisano obiekt bean sesji:
<enterprise-beans>
<session>
Obiekt bean EmployeeServiceAdmin stanowi implementację sesji
wykorzystywanej przez administratora aplikacji.
<ejb-name>EmployeeServiceAdmin</ejb-name>
<home>com.wombat.empl.EmployeeServiceAdminHome</home>
<remote>com.wombat.empl.EmployeeServiceAdmin</remote>
<ejb-class>com.wombat.empl.EmployeeServiceAdmin-Bean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Bean</transaction-type>
<resource-ref>
Referencja do bazy JDBC.
EmployeeService zawiera dziennik wszystkich transakcji
dokonywanych poprzez bean EmployeeService na potrzeby
audytu.
<res-ref-name>jdbc/EmployeeAppDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Containter</res-auth>
</resource-ref>
</session>
</enterprise-bean>
Elementy nie tylko izolują ten obiekt bean od wszelkich innych, ale również umożliwiają logiczne grupowanie danych. Element resource-ref zawiera informacje odpowiadające określonemu wpisowi związanemu ze środowiskiem. W ten sposób aplikacja przetwarzająca i wykorzystująca dane, a także programiści i administratorzy systemu zarządzający aplikacją mogą w prosty sposób zlokalizować i zaktualizować informacje o obiekcie bean lub serwerze EJB.
Większą grupą — i nie tak łatwo rozpoznawalną — jest element enterprise-beans. Dzięki niemu możliwe jest zamieszczenie informacji specyficznych dla danego pojemnika, a nie mających zastosowania do obiektów bean; informacje te nie zostaną wymieszane z tymi specyficznymi dla EJB. Jest to ważne rozgraniczenie; za jego pomocą w dalszej części rozdziału oddzielimy informacje konfiguracji serwera i klientów XML-RPC. Wreszcie — do tego elementu macierzystego można dodać dowolną liczbę obiektów bean; tutaj zamieszczony jest przykład tylko jednego obiektu sesji bean, ale można ich wstawić dowolnie wiele, tak by odpowiadały wielu obiektom bean w tworzonym pliku jar.
Choć pokazany plik XML został opisany pobieżnie, Czytelnik może już domyślać się, jak nazywać i organizować pliki XML tego rodzaju we własnych aplikacjach oraz w opisywanym przykładzie XML-RPC. Niemal każda aplikacja ma odmienne potrzeby, co pociąga za sobą odmienną organizację dokumentu XML oraz inny zestaw zawężeń. Skoro przyjrzeliśmy się już przykładowi konfigurowania serwera aplikacji za pomocą języka XML i zaczęliśmy zastanawiać się nad stworzeniem własnego pliku konfiguracyjnego, spróbujmy podejść do tematu praktycznie i zbudować właśnie taki plik dla naszych klas XML-RPC.
Aby zastosować naszą wiedzę w praktyce, zbudujemy plik konfiguracyjny XML dla klas XML-RPC, które napisaliśmy w poprzednim rozdziale. Posłuży nam to jako doskonały przykład zastosowania informacji konfiguracyjnych w formacie XML; mamy już dostępny parser XML (wykorzystany w serwerze XML-RPC) i możliwe jest użycie tego samego pliku konfiguracyjnego dla klientów i serwera. Co więcej, plik konfiguracyjny mógłby być edytowany za pomocą interfejsu IDE XML, a nie za pomocą własnego interfejsu do edycji pliku we własnym formacie. To wszystko przyczynia się do zmniejszenia ilości kodu koniecznego do budowania złożonych aplikacji.
Zanim zaczniemy pisać plik konfiguracyjny, musimy określić, jakie informacje będzie można z niego odczytać:
· port, na którym ma być uruchomiony serwer XML-RPC,
· klasa parsera, którą serwer wykorzysta jako sterownik SAX,
· procedury obsługi dla serwera XML-RPC,
· identyfikator klasy,
· nazwa klasy,
· nazwa hosta, z którym mają się łączyć klienty XML-RPC,
· port, z którym mają się łączyć klienty XML-RPC,
· klasa parsera, którą klient wykorzysta jako sterownik SAX.
To wszystkie informacje, jakie trzeba przekazać serwerowi i klientom, aby te mogły zostać uruchomione z tylko jednym parametrem — położeniem pliku konfiguracyjnego. Pamiętając o tym, zabierzmy się do pisania samego pliku konfiguracyjnego.
Podobnie jak w przypadku deskryptora wdrożeniowego EJB, nasz plik musi zawierać standardowy nagłówek XML. To chyba nie powinno przedstawiać trudności; poza tym trzeba jeszcze tylko pamiętać o określeniu przestrzeni nazw i elementu głównego dokumentu. Choć w środowisku produkcyjnym użylibyśmy przestrzeni nazw kojarzącej się z przeznaczeniem dokumentu (np. XMLRPC lub XmlRpcConfig), tutaj wciąż będziemy używać nazwy JavaXML, starając się zachować jednolity schemat we wszystkich przykładach w tej książce. Deklarujemy przestrzeń nazw tak jak we wcześniejszych rozdziałach. Element główny pełni rolę identyfikatora przeznaczenia dokumentu; tak więc nazwa xmlrpc-config będzie tutaj odpowiednia. Często jest tak — szczególnie w złożonych dokumentach XML — że najprostsze rozwiązanie jest rozwiązaniem najlepszym. Nazewnictwo elementów XML nie stanowi tutaj wyjątku.
Po podjęciu tych wstępnych decyzji można rozpocząć budowę pliku konfiguracyjnego dla posiadanych klas XML-RPC. Poniżej przedstawiona jest początkowa deklaracja XML oraz element główny:
<JavaXML:xmlrpc-config
xmlns:JavaXML="http://www.oreilly.com/catalog/javaxml/"
>
maciejle1