W niniejszym rozdziale:
Język Tea
Początki
Informacja o żądaniu
Administracja Tea
Zastosowania Tea
Aplikacja „Narzędzia”
Ostatnie słowo
Rozdział 14.
Szkielet Tea
W tym momencie można przejść do omawiania opartych na serwletach szkieletów służących do tworzenia zawartości stron WWW. Technika JSP zostanie omówiona później, ponieważ jest on najbardziej skomplikowaną alternatywą tworzenia zawartości. Jako pierwsza omówiona zostanie Tea. TeaServlet (znana powszechnie jako Tea) to produkt Walt Disney Internet Group (WDIG) — dawniej GO.com — rozwijany przez wiele lat wewnątrz firmy w celu wspomożenia tworzenia witryn o wysokim obciążeniu takich jak ESPN.com, NFL.com, Disney.com, DisneyLand.com, Movies.com, ABC.com i GO.com. Niedawno produkt ten został udostępniony jako Open Source w nadziei, że inni uznają Tea za narzędzie przydatne i pomogą w jego rozwoju. Strategia ta posiada sens — dzielenie się narzędziami w nadziei, że inni pomogą je wyostrzyć. Tu omówiona zostanie TeaServlet 1.1.0, dostępna pod adresem http://opensource.go.com. Wymaga ona Servlet API 2.1 i JDK 1.2 lub późniejszych. Licencja TeaServlet jest oparta na licencji Apache'a, jednej z najmniej restrykcyjnych i przez to atrakcyjnych dla programistów, ponieważ według niej można wykorzystywać Tea do tworzenia nowych produktów i witryn bez udostępniania tych produktów jako Open Source. Ten model licencji wykorzystywany jest przez wszystkie projekty Apache, włączając w to serwery Apache i Tomcat.
Tea została zaprojektowana dla zadań prowadzonych przez małe zespoły programistów i producentów technicznych. Role programisty to tworzenie „aplikacji” napisanych w Javie i zainstalowanych w TeaServlet. Producent tworzy i utrzymuje ostateczny wygląd dynamicznych stron WWW poprzez tworzenie szablonów Tea, które wywołują funkcje dostarczone przez aplikacje programisty. Na przykład, w witrynie ESPN, jeden programista tworzy aplikację służącą do zarządzania statystykami zespołów, inny (pracujący niezależnie) tworzy aplikację służącą do zarządzania statystykami zawodników, a producent techniczny umieszcza dane w sieci przy pomocy szablonów Tea. Właściwie kilku producentów pracujących niezależnie może wykorzystywać te same dane wspierające w celu utworzenie stron ukierunkowanych na różnych użytkowników — działanie, które firma WDIG wykonała w swoich witrynach włączając w to Disney.com, Movies.com, ESPN.com i GO.com. Szablony tworzone są w języku Tea w celu wymuszenia całkowitego oddzielenia zawartości od prezentacji.
Język Tea
Tea to język programowania zaprojektowany do formatowania tekstu. Posiada on silnie zaznaczone typy, musi być kompilowany i został zaprojektowany do pracy w środowisku opartym na Javie. Środowiskiem tym jest TeaServlet, narzędzie wykorzystujące Tea do tworzenia stron WWW i umożliwiające dostęp do mechanizmów serwletów, z których wywoływane są pliki szablonów Tea.
Głównym celem języka Tea jest wymuszenie oddzielenia zawartości i prezentacji, bez poświęcania podstawowych konstruktów programistycznych. Do czasu pisania tej książki, Tea została utworzona na podstawie czterech ograniczeń:
Dane i typy danych nie mogą być tworzone bezpośrednio. Są one odczytywane.
Odczytane dane nie mogą zostać bezpośrednio modyfikowane w żaden sposób.
Szablon nie może w bezpośredni sposób uszkadzać swojego środowiska.
Dostarczana jest jedynie minimalna ilość konstruktów programistycznych.
Ograniczenia te dodatkowo chronią system przed uszkodzonymi bądź błędnymi szablonami, co jest własnością niezwykle pożądaną w przypadku witryn stale przyjmujących dużą ilość szablonów, jak zdarza się to w popularnych witrynach informacyjnych. Projekt ten jest wygodny również dla firm hostingowych, które muszą dawać klientom możliwość tworzenia stron dynamicznych, ale nie chcą, aby żaden konkretny błąd klienta mógł wpłynąć negatywnie na pozostałą część serwera.
Ale czy kolejny język, zwłaszcza język, który potrafi o wiele mniej niż Java jest tak naprawdę potrzebny? Tea Template Language Manual (Podręcznik Języka Szablonów Tea) odpowiada na to pytanie:
Tea jest wynikiem wielu lat doświadczenia z innymi mechanizmami tworzenia stron WWW. Większość opartych WWW aplikacji rozpoczyna się od znaczników HTML osadzonych w kodzie, niezależnie od tego, czy jest to C, Perl, czy Java. Podejście to jest odpowiednie dla małych lub początkujących projektów, ponieważ nie wymaga dużej ilości programowania.
Ponieważ zmiany w formatowaniu strony mogą zdarzać się często, a twórcy nie chcą ich dokonywać, nieodwołalnie przesuwają się oni w stronę wykorzystywania pewnego rodzaju szablonowego mechanizmu wymiany elementów. Każdy element jest po prostu obszarem zablokowanym dla łańcucha, który zawiera dane utworzone przez aplikację. Te systemy szablonów dalej ewoluują w stronę obsługi specjalnych konstrukcji służących do formatowania tabeli, formularzy i prostej logiki warunkowej.
Podczas wprowadzania konstrukcji programistycznych do szablonu, podstawową trudnością jest stworzenie czegoś, co będzie posiadać wystarczającą moc, a zarazem będzie proste i bezpieczne. Jeżeli jest to zbyt potężne, wtedy kompletne aplikacje mogą być tworzone przez szablony. Jeżeli jest zbyt słabe, wtedy w aplikacji musi być zawarte formatowanie HTML. Jeżeli nie jest proste lub bezpieczne, wtedy programiści aplikacji musza sami stworzyć szablony.
Inaczej niż w przypadku istniejących języków osadzanych w czymś podobnym do ASP lub JSP, Tea jest językiem zaprojektowanym specjalnie do tego, aby spełniać wymagania systemu szablonów. Jest on bezpieczny, prosty, wydajny i potężny.
Każdy egzemplarz Tea jest zintegrowany ze specjalnym serwletem. Serwlet ten daje szablonom Tea kontrolę nad tworzeniem strony, zachowując silne powiązania z aplikacją wspierającą utworzoną przez programistę Javy. Podczas gdy serwlet ten dostarcza funkcjonalności podobnej do JSP, Tea wymusza poprawne rozdzielenie model-widok ze względu na międzynarodowe ograniczenia języka. Chociaż jest to także sugerowany model separacji w JSP, nie może on być w tamtym przypadku wymuszony. Dodatkowo szablony Tea nie obsługują własności programów, które mogą zostać wykorzystane nieodpowiedzialnie. Modyfikacje nie muszą przechodzić przez ścisłą korektę i fazę testów, która jest wymagana w przypadku JSP.
Każda osoba pracująca nad projektem powinna mieć dostęp do narzędzi pozwalających na pracę z maksymalną wydajnością, a Tea wykonuje swoją część pracy poprzez umożliwienie programiście wykonywania dokładnie tego, co jest mu potrzebne, tak łatwo jak to tylko możliwe, i żadnej dodatkowej pracy ponad to. Nawet w przypadku projektów tworzonych przez początkujących programistów wykorzystanie Tea posiada swoje zalety. Narzędzie wspomaga nabywanie dobrych praktyk programistycznych i ułatwia utrzymanie aplikacji.
Czytelnicy niniejszej książki powinni również pamiętać, że Tea nie jest językiem zaprojektowanym dla programistów, ale dla producentów technicznych. Jest językiem prostszym niż Java, bezpieczniejszym, ale tak samo wydajnym.
Początki
Aby zapoznać się z Tea, można przyjrzeć się kilku samodzielnym szablonom. Szablony te nie wykorzystują żadnych wspierających „aplikacyjnych” klas Javy, i w związku z tym nie mają wielkich możliwości działania. Poniżej przedstawiono pierwszy z szablonów:
<% template ProstaStrona() %>
To jest prosta strona, która nie robi praktycznie nic.
Powyższy szablon po prostu wyświetla „To jest prosta strona, która nie robi praktycznie nic.” każdemu, kto uzyska do niej dostęp. Szablony złożone są z obszarów kodu i tekstu. Obszary kodu są ograniczane znakami <% i %> (nie są wymagane żadne inne znaki ograniczające). Tekst poza obszarem kodu jest wyświetlany przez szablon bez żadnego formatowania, właśnie dlatego powyższy szablon po prostu wyświetla proste zdanie.
Aby uruchomić szablon, należy po pierwsze zapamiętać go w pliku o nazwie ProstaStrona.tea. Podobnie jak w klasach Javy, nazwa pliku musi być taka sama jak nazwa szablonu zadeklarowana przez konstruktora (łącznie z wielkością liter), a plik musi posiadać rozszerzenie .tea. Miejsce, w którym szablon powinien zostać zapisany, jest konfigurowalne. Poleca się katalog będący podkatalogiem WEB-INF, taki jak WEB-INF/szablony.
Następnym etapem przed uruchomieniem aplikacji jest instalacja samej Tea. Należy pobrać dystrybucję dostępną pod adresem http://opensource.go.com i wypełnić instrukcje instalacji w niej zawarte — umieścić plik TeaServlet.jar w ścieżce klas serwera WWW. Następnie należy dokonać edycji deskryptora web.xml aplikacji WWW w celu zarejestrowania TeaServlet pod nazwą tea z parametrem inicjacji określającym pozycję informacji o konfiguracji. Należy również skonfigurować zasadę odwzorowania przedrostków tak, że tea/* wywołuje serwlet tea. Przykład dodatku do web.xml jest przedstawiony w przykładzie 14.1.
Przykład 14.1.
Instalacja TeaServlet
<!-- ... -->
<servlet>
<servlet-name>
tea
</servlet-name>
<servlet-class>
com.go.teaservlet.TeaServlet
</servlet-class>
<init-param>
<param-name>
properties.file
</param-name>
<param-value>
<!—Należy dokonać edycji tak, aby wskazywało na bezwzględną ścieżkę do -->
<!—pliku właściwości-->
/tomcat/webapps/teatime/WEB-INF/TeaServlet.properties
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>
tea
</servlet-name>
<url-pattern>
/tea/*
</url-pattern>
</servlet-mapping>
<!-- ... -->
Jedyną informacją, którą należy dostosować, jest ścieżka do pliku TeaServlet.properties. Powinna być to ścieżka bezwzględna. Plik TeaServlet.properties definiuje zachowanie TeaServlet. Może być on stosunkowo długi, dlatego, że plik web.xml wskazuje na zewnętrzny plik zamiast bezpośrednio zawierać informacje konfiguracyjne. Pełne omówienie pliku właściwości nastąpi później, a teraz wykorzystany będzie najprostszy z możliwych plik TeaServlet.properties:
# TeaServlet.properties
#
# Określenie miejsca szablonów Tea
template.path = /tomcat/webapps/herbatka/WEB-INF/szablony
Proszę upewnić się co do umieszczenia pliku właściwości w miejscu określonym w pliku web.xml. Ostatnim krokiem jest ponowne uruchomienie serwera tak, aby mógł odczytać nowy plik web.xml (w przypadku niektórych serwerów nie jest to konieczne).
Kiedy TeaServlet zostanie właściwie zainstalowany, dostęp do ProstaStrona można uzyskać w miejscu takim jak http://localhost:8080/herbatka/tea/ProstaStrona. Część ścieżki /herbatka określa aplikację WWW, do której uzyskiwany jest dostęp. Następna część /tea jest tak sama, jak odwzorowanie przedrostków podane w web.xml herbatka, co powoduje uruchomienie TeaServlet w celu obsłużenia żądania. Szablony mogą również zostać umieszczone w podkatalogach pod template.path, w którym to przypadku wywoływane są przy pomocy ścieżki zawierającej podkatalogi takie jak /herbatka/tea/podkat/ProstaStrona.
Informacja o żądaniu
Tworzenie statycznej zawartości wiąże się z dużą ilością pracy, tak więc teraz utworzone zostanie coś bardziej dynamicznego. Konstruktor szablonu może przyjmować dowolną ilość parametrów, których wartości są automatycznie przekazywane z parametrów żądania (poprzez łańcuch zapytania i dane POST). Parametry te mogą być typu String, Integer, Float, Double i każdego innej podklasy java.lang.Number. Konwersja do typów numerycznych następuje automatycznie. Jeżeli nie zostanie przekazany żaden parametr o danej nazwie, lub konwersja typu nie powiedzie się, szablon otrzymuje wartość parametru null. Szablon przedstawiony w przykładzie 14-2 wykorzystuje swój konstruktor do przyjęcia parametru o nazwie nazwisko.
<% template ProstaStrona(String nazwisko) %>
To jest prosta strona, która nie robi praktycznie nic
poza wyświetleniem nazwiska: <% nazwisko ?>
Jeżeli uzyska się dostęp do szablonu jako herbatka/tea/ProstaStrona?imię=Janek, na stronie wyświetlone zostanie „Janek”. Jeżeli dostęp do szablonu nastąpi bez parametru nazwisko, na stronie wyświetlone zostanie „null”. Przykład 14.2 przedstawia szablon sprawdzający ten parametr.
Przykład 14.2.
Szablon sprawdzający nazwiska
<% template ProstaStrona(String nazwisko) %>
To jest prosta strona, która nie robi praktycznie nic
poza wyświetleniem nazwiska: <% nazwisko ?>
<P>
<%
if (nazwisko == null) {
"Nie podano nazwiska"
}
if (name == "Sędzia") {
"Proszę wstać"
}
%>
Powyższy nowy szablon sprawdza , czy nazwisko wynosi null i, jeżeli tak, wyświetla użytkownikowi ostrzeżenie. Szablon sprawdza również, czy podane nazwisko brzmi Sędzia i , jeżeli tak, traktuje użytkownika w specjalny sposób. Ważne jest, by zauważyć dwa elementy porównania Sędzia — po pierwsze wartości łańcuchów w Tea mogą zostać sprawdzone pod kątem równości przy pomocy operatora == (jak to jest w przypadku wszystkich obiektów). Oznacza to, że nie jest konieczne wyjaśnianie producentowi technicznemu celu metody .equals(). Po drugie, łańcuch null może być porównany z innym łańcuchem bez ryzyka wystąpienia wyjątku NullPointerException, co mogłoby się zdarzyć w przypadku .equals().
Sięganie głębiej
Inne informacje o żądaniu są dostępne przy pomocy TeaServlet. Na przykład, szablon Tea może uzyskać bezpośredni dostęp do nagłówków i cookies żądania, a także do parametrów. Przykład 14.3 przedstawia szablon Tea przeglądający informacje o żądaniu. W praktycznych przypadkach, rzadkością jest konieczność uzyskania przez szablon Tea bezpośredniego dostępu do tych informacji, zamiast tego można wykorzystać aplikację, która może dostarczyć przetworzoną wersję informacji.
Przykład 14.3.
Szablon przeglądający informacje o żądaniu
<% template Przegl() %>
<HTML><HEAD><TITLE>Przeglądanie</TITLE></HEAD><BODY>
<% zadanie = getRequest() // Typ obiektu jest narzucony %>
<H1>Różne informacje</H1>
QueryString: <% zadanie.queryString %> <BR>
RemoteUser: <% zadanie.remoteUser %> <BR>
<H1>Informacje o parametrach</H1>
<%
foreach (paramName in zadanie.parameters.names) {
paramName ": " zadanie.parameters[paramName] "<BR>"
}
%>
<H1>Informacja o atrybutach żądania</H1>
<%
foreach (attribName in zadanie.attributes.names) {
attribName ': ' zadanie.attributes[attribName] '<BR>'
}
%>
<H1>Header Informacja o nagłówkach</H1>
<%
foreach (headerName in zadanie.headers.names) {
headerName ': ' zadanie.headers[headerName] '<BR>'
}
%>
<h1>Informacja o cookies</h1>
<%
foreach (cookie in request.cookies.all) {
cookie.name ': ' cookie.value '<BR>'
}
%>
Wartość cookie identyfikatora sesji wynosi<% zadanie.cookies["JSESSIONID"].value %>.
<% // Rzeczy niemożliwe do wykonania w Tea... %>
<%/*
"<H1>Odczyt z sesji użytkownika</H1>"
zadanie.session["moze"] = "popracuje pozniej"
"<H1>Ustawienie atrybutu</H1>"
zadanie.attributes["nie"] = "będzie pracował"
"<H1>Konfiguracja cookie</H1>"
// Manipulacje odpowiedzią poprzez funkcje aplikacji
*/%>
</BODY></HTML>
Powyższy przykład może powiedzieć dużo o Tea. Po pierwsze, zmienne lokalne (takie jak zadanie) nie muszą być deklarowane według typu obiektu. Posiadają one silnie zaznaczony typ, ale przydzielanie typu jest ukryte. W języku istnieje słowo kluczowe isa pozwalające na obsługę kwestii związanych z tworzeniem zmiennych, jeżeli jest to konieczne.
Wykorzystane zostało wyrażenie foreach, które obsługuje często występujące zadanie wykonywania pętli nad zestawem obiektów. Jest to elastyczna konstrukcja, która może działać zarówno na tablicach, jak i na każdych innych obiektach będących rozszerzeniem java.util.Collection. Wyrażenie foreach obsługuje również działanie według zakresu i wstecz. Na przykład:
foreach (licznik in 1 .. 10 reverse) { licznik '<BR>' }
wyświetla odliczanie począwszy od 10. Wyrażenie foreach obsługuje w Tea wszystkie zadania związane z pętlami, nie występują wyrażenia for i while. (W związku z tym w Tea nie występują żadne pętle nieskończone.)
Wyrażenia w Tea nie muszą kończyć się średnikiem, jak to jest w przypadku Javy, ani też nie muszą występować w osobnych liniach. Zamiast tego, kompilator Tea wykorzystuje skonstruowaną uważnie gramatykę Tea w celu obliczenia końca wyrażeń! Średniki są obsługiwane w celu umożliwienia rzadkich okoliczności, w których średnik jest konieczny do uniknięcia dwuznaczności wyrażeń i jako ułatwienie dla programistów Javy, którzy instynktownie dodają średniki.
Zazwyczaj nie ma potrzeby wykorzystania w Tea operatora łączenia łańcuchów. Obecność łańcuchów w „podwójnych cudzysłowach” i 'pojedynczych cudzysłowach' wewnątrz kodu Tea dostarcza wystarczających wskazówek, w którym miejscu powinno wystąpić łączenie. W celu uzyskania bardziej zaawansowanego łączenia należy wykorzystać znak &. Znak + nie jest wykorzystywany, ponieważ jego nadmierne stosowanie powoduje niejasności.
Komentarze w Tea tworzone są według standardów Javy, w których /* i */ ograniczają komentarze o długości większej niż jedna linia, a // rozpoczyna komentarz jednoliniowy. Komentarze te nie są wysyłane do klienta, jeżeli pamięta się o umieszczeniu ich wewnątrz bloku kodu Tea.
W powyższym przykładzie wykorzystano długi komentarz w celu zademonstrowania działań, które nie mogą zostać wykonane przez Tea — odczyt z sesji użytkownika, ustawianie atrybutów żądania, konfiguracja cookie i właściwie wysyłanie czegokolwiek w żądaniu. Zdolność do uzyskania dostępu do sesji może zostać dodana w późniejszym czasie jako część procesu projektowania Open Source tworzącego Tea. Nie jest ona obecnie obsługiwana tylko dlatego, że WDIG wewnętrznie stosuje inny mechanizm śledzenia sesji. Inne własności celowo nie są obsługiwane i muszą zostać wykonane przez kod Javy w aplikacji wspierającej, z powodów założeń projektowych, które głoszą, że „otrzymane dane nie mogą być modyfikowane w żaden sposób”.
Administracja Tea
Szkielet TeaServlet wyświetla stronę WWW (napisaną oczywiście w Tea) służącą jako centrum administracyjne, przy pomocy której administrator witryny może zarządzać wszystkimi szablonami Tea i wspierającymi klasami aplikacji. Ekran administratora umożliwia przeglądanie aktualnie załadowanych szablonów i uruchomionych aplikacji, a także wykorzystanie funkcji udostępnionych przez te aplikacje w stylu Javadoc. Umożliwia również dostęp do dziennika zdarzeń dla szablonów, pozwalając administratorowi przeglądanie wszystkich dzienników lub ich wybranych części, w oparciu o szablon i poziom dziennika (debug, info, warn lub error). Ekran administracyjny dostarcza również mechanizmu służącego do kontrolowanego przeładowywania szablonów, które zostanie szczegółowo omówione w dalszej części. Rysunek 14.1 przedstawia przykładową stronę wyświetlającą szablony.
Rysunek 14.1. Administrowanie załadowanymi obecnie szablonami |
|
W celu uruchomienia aplikacji administracyjnej TeaServlet należy wcześniej skonfigurować plik TeaServlet.properties. Przykładowa konfiguracja przedstawiona jest w przykładzie 14.4.
Przykład 14.4. Początkowy plik TeaServlet.properties
# TeaServlet.properties
# Określenie lokalizacji szablonów Tea (plików .tea)
template.path = /tomcat/webapps/herbatka/WEB-INF/szablony
# Określenie opcjonalnej ścieżki do miejsca, W którym system będzie zapisywał
# skompilowane szablony jako pliki .class
template.classes = /tomcat/webapps/herbatka/WEB-INF/klasySzablony
# Określenie opcjonalnego domyślnego szablonu, który zostanie załadowany, jeżeli nie
# poda się innego
template.default = Indeks
# Określenie wspierających aplikacji, które zostaną załadowane do systemu TeaServlet
applications {
"System" {
# SystemApplication dostarcza wsparcia administracyjnego dla TeaServlet
class = com.go.teaservlet.AdminApplication
init {
# klucz bezpieczeństwa dla strony administracyjnej
admin.key = admin
admin.value = true
}
}
"Inne" {
class = WiecejNaTematAplikacjiPozniej
}
# Dla przykładu 14-8
"NarzedziaAp" {
class = NarzedziaAp
init {
narzedziaPlik = /tomcat/webapps/herbatka/WEB-INF/narzedzia.xml
}
}
}
# Określenie, które wiadomości aplikacji są umieszczane w dzienniku zdarzeń
log.debug = true
log.info = true
log.warn = true
log.error = true
Plik TeaServlet.properties konfiguruje system TeaServlet podobnie jak plik web.xml konfiguruje aplikację WWW. Plik ten podaje TeaServlet lokalizację szablonów oraz gdzie powinny zostać umieszczone skompilowane klasy szablonów tak, aby mogły być przechowywane podczas ponownych uruchomień serwera. Zawiera także informacje, które aplikacje powinny być załadowane dla wsparcia szablonów oraz który poziom wiadomości dziennika zdarzeń powinien zostać domyślnie zastosowany. Każda aplikacja musi zostać zarejestrowana w TeaServlet.properties przy pomocy określonego pliku klasy oraz opcjonalnie określonymi parami parametrów nazwa-wartość.
Struktura TeaServlet.properties jest podobna do tradycyjnego pliku java.util.Properties, z kilkoma poważnymi ulepszeniami — zachowano kolejność elementów, cudzysłowy (pojedyncze lub podwójne) mogą zostać wykorzystane do zdefiniowania kluczy, w których osadzone są spacje, a własności mogą zostać zagnieżdżone przy pomocy zakręconych nawiasów. Przykład 14.4 mógłby zostać napisany jako zwykły plik właściwości Javy, jak przedstawiono poniżej (chociaż po odczytaniu go przez java.util.Properties stracony zostałby porządek elementów). Plik ten mógłby zostać napisany przy pomocy XML, chociaż dla prostych zastosowań takich jak to XML byłby niepotrzebnym utrudnieniem.
template.path = /tomcat/webapps/herbatka/WEB-INF/szablony
template.classes = /tomcat/webapps/herbatka/WEB-INF/klasySzablony
applications.System.class = com.go.teaservlet.AdminApplication
applications.System.admin.key = admin
applications.System.admin.value = true
applications.Inne.class = WiecejNaTematAplikacjiPozniej
log.debug = true
log.info = true
log.warn = true
log.error = true
Po dokonaniu edycji pliku TeaServlet.properties i ponownym uruchomieniu serwera, system jest gotowy do działań administracyjnych. Domyślnym położeniem aplikacji administracyjnej (zakładając, że została ona umieszczona w kontekście /herbatka) jest http://localhost:8080/herbatka/tea/system/teaservlet/Admin?admin=true. Wyrażenie ?admin=true działa jako prymitywne zabezpieczenie. Nazwa i wartość muszą zgadzać się z wartościami admin.key/admin.value z pliku TeaServlet.properties. Tak naprawdę nic by się nie stało, jeżeli człowiek z zewnątrz uzyskałby dostęp do stron administracyjnych, ale pozostawienie frontowych drzwi otwartych nie jest mądre, tak więc istnieje prymitywny zamek. Przypuszczalnie społeczność Open Source szybko poprawi mechanizm bezpieczeństwa.
Najczęstszym zastosowaniem dla aplikacji administracyjnej jest ponowne ładowanie plików szablonów. Szablony nie są przeładowywane podczas otwierania, jak strony JSP. Zmiany w szablonach nie są widoczne, dopóki nie zostanie naciśnięty guzik „Reload Changes” („Przeładuj zmiany”) na ekranie administratora Templates (Szablony). Kompilowanie szablonów tylko na żądanie jest bardziej wydajne niż sprawdzanie znacznika czasu podczas żądania oraz pozwala także na wystąpienie wyraźniejszego kroku „opublikowanie” aktywnej witrynie. Aplikacja administracyjna nie przyjmuje zmian, jeżeli szablonu nie uda się skompilować, pozostawiając poprzednią wersję, tak więc klienci absolutnie nie mogą zobaczyć błędu kompilacji. Wszystkie błędy występujące podczas kompilacji zostają wyświetlone przez aplikację administracyjną, jak przedstawiono na rysunku 14.2.
Rysunek 14.2. Obszerne i precyzyjne wiadomości o błędach kompilacji |
|
Proszę zauważyć, że lokalizacja błędów i ich opis jest są absolutnie precyzyjne, TeaServlet może to uczynić, ponieważ szablony Tea są kompilowane bezpośrednio do kodu bajtowego Javy! Nie jest tworzony żaden pośredni plik .java, a w związku z tym nie jest konieczne zastosowanie żadnego kompilatora Javy. Kompilowanie szablonów bezpośrednio do kodu bajtowego jest wykonywane przy pomocy technik JIT i HotSpot w celu osiągnięcia maksymalnej wydajności, ale inaczej niż w przypadku stron JSP, kompilator pracuje bezpośrednio na źródle utworzonym przez człowieka i w związku z tym potrafi lepiej zdiagnozować błędy bez pośredniczącego pliku rozmywającego bezpośredniość. Poza tym, Tea nie wymaga dołączenia kompilatora Javy do serwera WWW, który jest potrzebny w przypadku JSP. Wymaganie to sprawia problemy związane z licencjonowaniem. Tea może również prekompilować szablony, umożliwiając dystrybucję szablonów bez ich źródeł.
Skompilowane szablony: szansa dla biznesu
Uwaga!!!!
Możliwość dystrybucji prekompilowanych szablonów Tea tworzy nowy, interesujący model biznesowy. Producent tworzący aplikację WWW przy pomocy Tea może sprzedawać aplikację WWW bez udostępniania oryginalnego kodu źródłowego szablonów. Potrzebna jest jedynie biblioteka Tea, która posiada możliwość przenoszenia pomiędzy serwerami i może być dołączana do aplikacji. Możliwość udostępniania produktu bez kodów źródłowych może spowodować, że niektórzy producenci utworzą nowe aplikacje WWW na sprzedaż.
Proszę zauważyć, że JSP również obsługuje prekompilowanie stron. Niestety, kod bajtowy tworzony przez JSP jest często uzależniony od klas związanych z danym kontenerem, a tworzona klasa musi być zgodna z zależną od kontenera konwencją nadawania nazw (proszę zobaczyć przykład 18-2 z rozdziału 18, „JavaServer Pages”). Własność ta powoduje przywiązanie prekompilowanych stron JSP do konkretnego kontenera.
Tea elegancko obsługuje również błędy czasu wykonania. Wyjątki, które wystąpią wewnątrz Tea zawierają ścieżki, w których znajdują się numery linii szablonów Tea, na przykład:
2001/10/23 19:29:12.105 GMT> java.lang.NullPointerException:
at com.go.teaservlet.template.ProstaStrona.execute(ProstaStrona.tea:5)
at java.lang.reflect.Method.invoke(Native Method)
at co.go.tea.runtime.TemplateLoader$TemplateImpl.execute(TemplateLoader.java, Compiled Code)
at com.go.teaservlet.TeaServlet.processTemplate(TeaServlet.java, Compiled Code)
at com.go.teaservlet.TeaServlet.doGet(TeaServlet.java:238)
<itd.>
Wiadomości takie jak powyższa mogą być przeglądane przy pomocy aplikacji administracyjnej TeaServlet. Kiedy występują, klient domyślnie otrzymuje stronę błędu o kodzie stanu 500, celowo bez dodatkowych informacji w celu zachowania bezpieczeństwa.
Tea posiada również własność „strażnik wyjątków”. Własność ta pozwala Tea na kontynuowanie przetwarzania strony podczas zapisywania błędu w dzienniku. Tea tworzy wszystkie części strony, które może omijając niewielką część, na którą wpływ ma wyjątek. Dla wielu witryn strona, której brakuje niewielkiej części jest lepsza niż błąd serwera zwracany użytkownikowi.
Zastosowania Tea
Jak dotychczas omawiane były samotne szablony Tea, które są bardzo słabe — celowo słabe. Ale szablony Tea są jak frontowi żołnierze piechoty — kiedy samotni, słabi, ale kiedy w zespole z zaawansowanym wsparciem lotniczym, niezwykle potężni. Poniżej opisano sposoby uczynienia szablonów Tea potężniejszymi.
Szablony mogą uzyskać dostęp do każdej funkcji z dowolnej „aplikacji” zainstalowanej w TeaServlet poprzez plik TeaServlet.properties. (Każda aplikacja WWW może posiadać inny plik TeaServlet.properties i w związku z tym inny zestaw zainstalowanych aplikacji.) Szablony wywołują funkcję poprzez wymienienie w bloku kodu nazwy funkcji, po której następuje para nawiasów. Funkcje mogą przyjmować parametry i zwracać wartości:
<% lista = pobierzRekordy() %>
<% usunRekord(8086) %>
Ekran administracyjny TeaServlet wyświetla listę wszystkich dostępnych funkcji z wszystkich zainstalowanych aplikacji. Jeżeli dwie aplikacje deklarują tę samą funkcję, szablon może poprzedzić wywołanie funkcji nazwą aplikacji w celu uniknięcia niejasności:
<% lista = ApListaRekordów.pobierzRekordy() %>
Istnieje zbiór klas narzędziowych dostępnych dla wszystkich szablonów utworzonych w celu wspomożenia w podstawowych zadaniach przetwarzania tekstu. Poniższe podrozdziały wymieniają niektóre z najczęściej wywoływanych funkcji narzędziowych.
Przetwarzanie tekstu
Poniżej przedstawiono niektóre popularne funkcje przetwarzające tekst.
Date currentDate()
Zwraca aktualną datę.
void dateFormat (String format)
Zmienia format wszystkich dat wyświetlanych w późniejszym czasie przez ten szablon.
void numberFormat (String format)
Zmienia format wszystkich liczb wyświetlanych w późniejszym czasie przez ten szablon (wyłączając liczby zawarte dosłownie w łańcuchach).
void nullFormat (String format)
Przydziela łańcuch, który zostanie wykorzystany zamiast null podczas wyświetlania zmiennej o wartości null.
void setLocale(String jezyk, String kraj)
Zmienia lokalizację dla wszystkich dat, liczb itp. wyświetlanych w późniejszym czasie przez ten szablon.
boolean startsWith(String lan, String przedrostek)
Zwraca true, jeżeli łańcuch rozpoczyna się od określonego przedrostka.
boolean endsWith(String lan, String przyrostek)
Zwraca true, jeżeli łańcuch kończy się określonym przyrostkiem.
String subString(String lan, int poczIndeks, int konIndeks)
Zwraca część łańcucha od indeksu początkowego do końcowego (wyłączając indeksy).
String trim (String lan)
Zwraca łańcuch bez początkowych i końcowych pustych miejsc
int[] find(String lan, String szuka)
int[] find(String lan, String szuka, int odIndeks)
Zwraca indeksy, w których łańcuch zawiera poszukiwany String, z opcjonalnym indeksem początkowym.
String replace(String zrodlo, String wzor, String zamiana)
String replace(String zrodlo, String wzor, String zamiana int odIndeks)
String replace(String zrodlo, String wzor, String zamiana int odIndeks int
doIndeks)
Zamienia wszystkie dane dokładnie pasujące do wzoru na podany łańcuch zamiany, z opcjonalnym indeksem początkowym i końcowym.
Obsługa zawartości
Poniżej wymieniono popularne metody obsługujące zawartość.
void InsertFile (string nazwapliku)
Wstawia zawartość danego pliku do wyświetlanej strony. Nie wstawia nic, jeżeli plik nie może zostać odczytany. Nazwa pliku może być względna lub bezwzględna. Jeżeli bezwzględna, ścieżka jest odnogą katalogu macierzystego szablonów (zazwyczaj katalogu macierzystego dokumentów serwera WWW).
void InsertURL (string url)
Wstawia zawartość danego URL-a do wyświetlanej strony. Nie wstawia nic, jeżeli URL nie może zostać odczytany. URL może być względny lub bezwzględny.
boolean fileExists(String nazwapliku)
Zwraca true, jeżeli plik o podanej nazwie istnieje.
boolean URLExists(String url)
Zwraca true, jeżeli podany URL istnieje.
Obsługa żądań/odpowiedzi
Dodatkowo przedstawiono kilka często wykorzystywanych metod żądań/odpowiedzi.
com.go.teaservlet.HttpContext.Request getRequest()
com.go.teaservlet.HttpContext.Request getRequest(String kodowanie)
Zwraca obiekt zawierający informacje o żądaniu, z opcjonalnym kodowaniem pozwalającym na automatyczną konwersję wartości parametrów.
void setContentType(String typZawartosci)
Ustawia typ zawartości odpowiedzi.
void setHeader(String nazwa, String wartosc)
Ustawia nagłówek odpowiedzi.
void setStatus(int ks)
Ustawia kod stanu odpowiedzi.
void sendRedirect(String url)
Przekierowuje żądanie do danego URL-a.
void sendError(int ks)
void sendError(int ks, String wiadomosc)
Wysłanie strony z błędem o danym kodzie stanu z opcjonalną wiadomością o błędzie.
Szablon w przykładzie 14.5 demonstruje sposób wywoływania klas narzędziowych przez szablon w celu wyświetlenia danych procentowych dla klikania na reklamy danego dnia. Data i procenty formatowane są w stylu francuskim.
Przykład 14.5.
Francuskie klikanie
<%template klik() %>
<HTML<HEAD><TITLE>Testowanie funkcji wbudowanych w Tea</TITLE></HEAD><BODY>
<%
// Ustawienie lokalizacji. Nie zmienia się ona dla wszystkich danych wyświetlanych
// później przez szablon
set locale ("fr", "CA") // Quebec
// Określenie formatowania dat I liczb
dateFormat("d MMMM yyyy")
numberFormat("#0,00%")
// Pewne fałszywe dane
klik- 485.0
dostep = 7673.0
%>
<H2><% currentDate %></H2>
Le pourcentage: <% klik/dostep %>
</BODY></HTML>
Szablon wyświetla dane podobne do poniższych, z datą i liczbami właściwymi dla kogoś, kto mieszka w Quebecu:
23 octobre 2001
Le pourcentage: 6,32%
Tworzenie aplikacji Tea
Teraz przedstawiony zostanie sposób tworzenia własnej aplikacji Tea. Jest to łatwe — należy po prostu napisać klasę aplikacji i klasę kontekstu. Wszystkie klasy aplikacji są rozszerzeniem interfejsu com.go.teaservlet.Application. Podobnie jak serwlet, interfejs Application posiada metody init() i destroy() obsługujące kwestie związane z okresem trwałości. Zamiast metody service() Application posiada parę metod createContext() i getContextType(). Metoda createContext() zwraca obiekt kontekstu, którego metody zostają udostępnione szablonom jako funkcje. Metoda getContextType() zwraca java.lang.Class obiektu zwracanego przez createContext() w celu ułatwienia sprawdzania typu.
Poniższe dwa przykłady przedstawiają prostą aplikację próbującą na różne sposoby określić nazwę użytkownika. Klasa NazwaAp to klasa aplikacji (przykład 14.6); klasa NazwaKontekst to obiekt kontekstu tworzony przez NazwaAp i udostępniany szablonom (przykład 14.7).
Przykład 14.6.
Klasa NazwaAp
import com.go.teaservlet.*;
import javax.servlet.*;
public class NazwaAp implements Application {
// Zarówno init(ApplicationConfig) jak i destroy() muszą być zaimplementowane ponieważ
// zostały zadeklarowane w interfejsie Application interface. Mogą pozostać puste.
public void init(ApplicationConfig konfig) throws ServletException {
}
// Tworzenie kontekstu dostarcza funkcji dostępnych z szablonów.
public Object createContext(ApplicationRequest zadanie,
ApplicationResponse odpowiedz) {
// Często przekazuje się żądanie i odpowiedź, nawet jeżeli nie są wykorzystywane,
// ponieważ mogą być wykorzystane później
return new NameContext(zadanie, odpowiedz);
}
// Ta metoda musi zostać zaimplementowana, by zwracać klasę obiektu zwracanego przez
// createContext()
public Class getContextType() {
return NameContext.class;
}
public void destroy() {
}
}
TeaServlet wywołuje metodę init() jeden raz, kiedy aplikacja zostaje załadowana po raz pierwszy, w celu dania jej możliwości przeprowadzenia inicjacji. Wywołuje jeden raz destroy() podczas kończenia pracy w celu dania aplikacji możliwości wyczyszczenia. Metoda init() przyjmuje parametr ApplicationConfig, który jest podinterfejsem ServletConfig z trzema dodatkowymi metodami — getLog(), getName() i getProperties() — służącymi do odczytywania dziennika zdarzeń, nazwy i właściwości inicjacji.
TeaServlet wywołuje metodę createContext() przed obsługą żądań i udostępnia wszystkie metody zwracanego obiektu kontekstu jako funkcje żądanego szablonu Tea. Jeżeli każdy użytkownik musi posiadać swój własny kontekst, może to być w łatwy sposób osiągnięte przy pomocy standardowego śledzenia sesji w metodzie createContext(). Metoda CreateContext() przyjmuje jako parametry ApplicationRequest i ApplicationResponse. Są to podinterfejsy HttpServletRequest i HttpServletResponse z dodatkiem kilku metod Tea.
Przykład 14.7.
Klasa NazwaKontekst
import com.go.teaservlet.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class NazwaKontekst {
ApplicationRequest zadanie;
ApplicationResponse odpowiedz;
String nazwa;
public NazwaKontekst(ApplicationRequest zadanie,
ApplicationResponse odpowiedz) {
this.zadanie = zadanie;
this.odpowiedz = odpowiedz;
}
public String pobierzNazwa() {
// Jeżeli nazwa wcześniej określona, zwrócenie jej
if (nazwa != null) {
return nazwa;
}
// Próba określenia nazwy użytkownika
nazwa = zadanie.getRemoteUser();
// Jeżeli nazwa logowania nie jest dostępna, próba odczytania parametru
if (nazwa == null) {
nazwa = zadanie.getParameter("nazwa");
}
// Jeżeli nazwa nie jest dostępna jako parametr, próba sesji
if (nazwa == null) {
nazwa = (String) zadanie.getSession().getAttribute("nazwa");
}
// Jeżeli nazwy nie ma w sesji, próba cookie
if (nazwa == null) {
Cookie[] cookies = zadanie.getCookies();
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].pobierzNazwa().equals("nazwa")) {
nazwa = cookies[i].getValue();
}
}
}
// Jeżeli nazwy nie ma też w sesji, poddanie się
return nazwa;
}
}
Metoda pobierzNazwa() próbuje określić nazwę użytkownika-klienta poprzez przeszukanie danych logowania, listy parametrów, danych sesji i cookies żądania. Zwraca null, jeżeli odnalezienie informacji o nazwie nie jest możliwe. Szablony uzyskują dostęp do nazwy przez wywołanie funkcji pobierzNazwa(). Proszę zauważyć, że kontekst określa nazwę klienta w metodzie pobierzNazwa(), a nie w konstruktorze. Jest to sztuczka mająca na celu poprawienie wydajności. Jeżeli logika znajdowałaby się w konstruktorze, byłaby wykonywana podczas obsługi każdego żądania, niezależnie od tego, czy szablon wywoływałby pobierzNazwa(), czy nie.
Uczynienie aplikacji NazwaAp dostępną wymaga następującego krótkiego dodatku do pliku TeaServlet.properties:
"NazwaAp" {
class = NazwaAp
}
Można sprawdzić, czy aplikacja została prawidłowo załadowana przez przejrzenie łącza Applications (Aplikacje) aplikacji administracyjnej TeaServlet. Po załadowaniu tej aplikacji każdy szablon może wyświetlić nazwę klienta przy pomocy funkcji pobierzNazwa():
<% template nazwy() %>
<%
setContentType("text/plain")
nullFormat("Nieznane")
%>
Nazwa użytkownika to <%pobierzNazwa() %>
Aplikacja „Narzędzia”
Określanie nazwy użytkownika nie jest zbyt realistycznym zastosowaniem, tak więc opisane teraz zostanie coś bliższego rzeczywistości. Utworzona zostanie aplikacja, która wyświetla listę różnych dostępnych narzędzi do tworzenia zawartości (aplikacja mniej więcej taka, jakiej potrzebuje Servlets.com). Informacje o narzędziach będą pochodzić z pliku XML, chociaż równie dobrze mogłyby pochodzić z bazy danych. Przykład 14.8 przedstawia klasę NarzedziaAp.
Przykład 14.8.
Centrum aplikacji „Narzędzia”
import com.go.teaservlet.*;
import com.go.trove.log.*;
import java.sql.Timestamp;
import java.util.*;
import javax.servlet.*;
public class NarzedziaAp implements Application {
private Log dziennik;
private Narzedzie[] narzedzia;
public void init(ApplicationConfig konfig) throws ServletException {
// Dziennik zdarzeń dla wydarzeń specyficznych dla tej aplikacji
dziennik = konfig.getLog();
// Pobranie danych o narzędziach w metodzie init() dla zachowania prostoty
String narzedziaPlik = konfig.getInitParameter("narzedziaPlik");
if (narzedziaPlik == null) {
throw new ServletException(
"Plik danych o narzędziach musi zostać określony jako parametr inicjacji
narzedziaPlik");
}
dziennik.debug("Pobieranie narzędzi z " + narzedziaPlik);
try {
narzedzia = Narzedzie.pobierzNarzedzia(narzedziaPlik);
if (nazredzia.length == 0) {
dziennik.warn("Nie znaleziono narzędzi w " + narzeziaPlik);
}
}
catch (Exception w) {
dziennik.error(w);
throw new ServletException(e);
}
}
public Object createContext(ApplicationRequest zadanie,
ApplicationResponse odpowiedz) {
return new NarzedziaKontekst(zadanie, odpowiedz, this);
}
public Class getContextType() {
return NarzedziaKontekst.class;
}
public void destroy() {
}
public Narzedzie[] pobierzNarzedzia() {
// Zazwyczaj „aplikacja” utrzymywałaby lub miałaby dostęp do wcześniej utworzonej
// bazy danych. Tutaj dla zachowania prostoty zastosowano plik XML
return narzedzia;
}
public Narzedzie[] pobierzNarzedzie(String stan) {
// Zwracane tylko narzędzi o określonym stanie
// (wysłane, żyje, odrzucone, lub martwe)
List lista = new LinkedList();
for (int i = 0; i < narzedzia.length; i++) {
if (narzedzia[i].pobierzZnacznikStanu().equalsIgnoreCase(stan)) {
lista.add(narzedzia[i]);
}
}
return (Narzedzie[]) lista.toArray(new Narzedzie[0]);
}
}
Metoda init() NarzedziaAp wykorzystuje przekazany jej ApplicationConfig w celu odczytania i zapisania odwołania do dziennika zdarzeń, następnie wykorzystuje konfig w celu pobrania parametru inicjacji narzedziaPlik. Wartość tego parametru musi zostać określona w pliku TeaServlet.properties:
"NarzedziaAp" {
class = NarzedziaAp
init {
narzedziaPlik = /tomcat/webapps/herbatka/WEB-INF/narzedzia.xml
}
}
Metoda init() wywołuje następnie pobierzNarzedzia() w celu pobrania informacji o narzędziach z pliku XML do tablicy narzędzi. Plik XML powinien wyglądać podobnie do przykładu 14.9.
<?xml version="1.0"?>
<narzedzia>
<narzedzie id="1">
<nazwa>JavaServer Pages</nazwa>
<domURL>http://java.sun.com/products/jsp</domURL>
<komentarz>
JavaServer Pages (JSP) to technologia utworzona przez Sun Microsystems, blisko związana z serwletami. Tak jak w przypadku serwletów, Sun udostępnia specyfikację JSP i inni producenci rywalizują swoimi implementacjami standardu. To, że jest dziełem firmy Sun, stawia JSP w bardzo uprzywilejowanej pozycji i jeżeli JSP rozwiązywałaby wystarczającą ilość problemów użytkowników, przypuszczalnie zagarnęłaby rynek jeszcze przed pojawieniem się alternatyw. Jednak zadziwiająco duża liczba użytkowników jest rozczarowana JSP i jej alternatywy zyskują popularność.
</komentarz>
<znacznikStanu>ŻYJE</znacznikStanu>
<czasUtworzenia>1998-03-17 00:00:00.000</czasUtworzenia>
<czasModyfikacji>1999-12-16 00:00:00.000</czasModyfikacji>
</narzedzie>
<narzedzie id="2">
<nazwa>Tea</nazwa>
<domURL>http://opensource.go.com</domURL>
<komentarz>
Tea to nowy produkt Open Source Walt Disney Internet Group, tworzony wewnętrznie przez lata w celu rozwiązania problemów z tworzeniem witryn takich jak ESPN.com. Tea jest podobna do JSP, ale pozbawiona jej wielu wad, i już posiada ogromną ilość narzędzi wspierających.
</komentarz>
<znacznikStanu>ŻYJE</znacznikStanu>
<czasUtworzenia>2000-07-12 00:00:00.000</czasUtworzenia>
<czasModyfikacji>2000-07-12 00:00:00.000</czasModyfikacji>
</narzedzie>
<narzedzie id="3">
<nazwa>WebMacro</nazwa>
<domURL>http://jakarta.apache.org</domURL>
<komentarz>
WebMacro to mechanizm szablonów utworzony przez firmę Semiotek jako część projektu Shimari, włączony teraz do Apache Jakarta Project. WebMacro było wykorzystywane w komercyjnych witrynach takich jak AltaVista.com, zintegrowane w szkieletach Open Source takich jak Turbine i Melati oraz wykorzystywane w znanych projektach Open Source takich jak JetSpeed.
</komentarz>
<znacznikStanu>ŻYJE</znacznikStanu>
<czasUtworzenia>1998-11-19 00:00:00.000</czasUtworzenia>
<czasModyfikacji>2000-08-31 00:00:00.000</czasModyfikacji>
</narzedzie>
<narzedzie id="4">
<nazwa>Element Construction Set</nazwa>
<domURL>http://java.apache.org/ecs</domURL>
<komentarz>
Pakiet Element Construction Set (ECS) pochodzący z Java Apache Project to zbiór klas utworzonych na podstawie htmlKona, produktu WebLogic (teraz BEA Systems). ECS posiada wiele ograniczeń, ale rozwiązuje konkretny zestaw problemów.
</komentarz>
<znacznikStanu>ŻYJE</znacznikStanu>
<czasUtworzenia>1999-03-31 00:00:00.000</czasUtworzenia>
<czasModyfikacji>2000-06-16 00:00:00.000</czasModyfikacji>
</narzedzie>
<narzedzie id="5">
<nazwa>XMLC</nazwa>
<domURL>http://xmlc.enhydra.org</domURL>
<komentarz>
XMLC wykorzystuje XML w celu osiągnięcia niemal tej samej mocy, co ECS, bez wielu jego ograniczeń. Technika ta została utworzona przez Lutris jako część ich serwera Open Source — Enhydra Application Server, może być stosowana jako osobny element.
</komentarz>
<znacznikStanu>ŻYJE</znacznikStanu>
<czasUtworzenia>1998-10-11 00:00:00.000</czasUtworzenia>
<czasModyfikacji>2000-03-09 00:00:00.000</czasModyfikacji>
</narzedzie>
<!—itd. -->
</narzedzia>
Aplikacja odczytuje powyższy plik przy pomocy interfejsu JDOM, interfejsu Open Source służącego do odczytu, zapisu i manipulacji XML przy pomocy Javy. Jeżeli zamiast tego wyniki powinny być odczytywane z bazy danych, można to uczynić po prostu zamieniając SAXBuilder JDOM (który tworzy Document z pliku lub potoku przy pomocy analizatora SAX) na ResultSetBuilder JDOM (który tworzy Document z java.sql.ResultSet, klasy przekazanej JDOM). W trakcie pisania niniejszej książki, JDOM ciągle był w fazie tworzenia. Niniejszy przykład wykorzystuje JDOM Beta 5. W celu pracy z ostateczną wersją JDOM mogą się okazać konieczne drobne zmiany.
Metoda createContext() konstruuje nawy egzemplarz NarzedziaKontekst. Kod NarzedziaKontekst jest przedstawiony w przykładzie 14.10.
Przykład 14.10.
Kontekst zawierający wszystkie dostępne funkcje
import com.go.teaservlet.*;
public class NarzedziaKontekst {
ApplicationRequest zadanie;
ApplicationResponse odpowiedz;
NarzedziaAp ap;
public NarzedziaKontekst(ApplicationRequest zadanie,
ApplicationResponse odpowiedz,
NarzedziaAp ap) {
this.zadanie = zadanie;
this.zadanie = zadanie;
this.ap = ap;
}
public Narzedzie[] pobierzNarzedzia() {
return ap.pobierzNarzedzia();
}
public Narzedzie[] pobierzNarzedzia(String stan) {
return ap.pobierzNarzedzia(stan);
}
}
Metody publiczne w kontekście są funkcjami udostępnianymi szablonom Tea. W tym miejscu metody odwołują się z powrotem do aplikacji, w można powiedzieć dobrym stylu, ponieważ konteksty powinny być tak małe, jak to jest tylko możliwe; stan powinien być przechowywany w aplikacji. Klasa Narzedzie sama działa jako magazyn informacji Narzedzie. Jej kod przedstawiony jest w przykładzie 14.11.
Przykład 14.11.
Klasa pobierająca i przechowująca dane Narzedzie
import java.io.*;
import java.sql.*;
import java.util.*;
import org.jdom.*;
import org.jdom.input.*;
public class Narzedzie {
// Dane na temat tego rekordu narzedzie
public int id;
public String nazwa;
public String domURL;
public String komentarz;
public String znacznikStanu;
public Timestamp czasUtworzenia;
public Timestamp czasModyfikacji;
// Tea może uzyskać dostęp jedynie do podstawowych własności, potrzebne są więc
// specjalne metody dostępu
public int pobierzId() { return id; }
public String pobierzNazwa() { return nazwa; }
public String pobierzDomURL() { return domURL; }
public String pobierzKomentarz() { return komentarz; }
public String pobierzZnacznikStanu() { return znacznikStanu; }
public Timestamp pobierzCzasUtworzenia() { return czasUtworzenia; }
public Timestamp pobierzCzasModyfikacji() { return czasModyfikacji; }
public int pobierzCzasUtworzeniaDni() {
return (int) ((System.currentTimeMillis() - czasUtworzenia.getTime()) /
(24 * 60 * 60 * 1000)); // milisekundy w dniu
}
public int pobierzCzasModyfikacjiDni() {
return (int) ((System.currentTimeMillis() - czasModyfikacji.getTime()) /
(24 * 60 * 60 * 1000)); // milisekundy w dniu
}
// Idealnie użyto by metod takich jak te, ale Tea pozwala jedynie na dostęp
// do własności obiektu. Nie będą one widoczne.
public boolean czyNowy(int dni) {
return pobierzCzasUtworzeniaDni() < dni;
}
public boolean czyUaktualniony(int dni) {
return pobierzCzasModyfikacjiDni () < dni;
}
public static Narzedzie[] pobierzNarzedzia(String plikNarzedzia) throws Exception {
// Odczytanie danych narzędzi z pliku XML zawierającego elementy <narzedzie>
// Wykorzystanie interfejsu JDOM by uprościć to działanie (http://jdom.org)
List obiektyNarzedzia = new LinkedList();
SAXBuilder budowa = new SAXBuilder();
Document dokument = budowa.build(new File(plikNarzedzia));
Element root = dokument.getRootElement();
List elementyNarzedzia = root.getChildren("narzedzie");
Iterator i = elementyNarzedzia.iterator();
while (i.hasNext()) {
Element narzedzie = (Element) i.next();
Narzedzie n = new Narzedzie();
n.id = narzedzie.getAttribute("id").getIntValue();
n.nazwa = narzedzie.getChild("nazwa").getTextTrim();
n.domURL = narzedzie.getChild("domURL").getTextTrim();
n.komentarz = narzedzie.getChild("komentarz").getTextTrim();
n.znacznikStanu = narzedzie.getChild("znacznikStanu").getTextTrim();
n.czasUtworzenia = Timestamp.valueOf(
narzedzie.getChild("czasUtworzenia").getTextTrim());
t.czasModyfikacji = Timestamp.valueOf(
narzedzie.getChild("czasModyfikacji").getTextTrim());
obiektyNarzedzia.add(n);
}
return (Narzedzie[]) obiektNarzedzia.toArray(new Narzedzie[0]);
}
}
Proszę zauważyć, że chociaż klasa powyższa posiada publiczne zmienne przechowujące jej stan, to nie są one widoczne przez szablony Tea. Szablony Tea mają dostęp jedynie do funkcji zadeklarowanych w zainstalowanych kontekstach i do podstawowych własności obiektów zwracanych przez te funkcje. Dlatego klasa Narzedzie posiada różne metody dostępu. Narzedzie posiada również dwie metody ułatwiające, które zwracają ilość dni od utworzenia i modyfikacji rekordu. Można by pomyśleć o wykorzystaniu metody isNewWithin(int dni) lub isUpdateWithin(int dni). Jednak szablon nie posiada dostępu do tych metod, ponieważ nie należą one do podstawowych własności obiektu.
Przykład 14.12 przedstawia prosty fronton szablonu dla tej aplikacji. Nosi on nazwę widoknarz1 i przyjmuje opcjonalny parametr stan, przy pomocy którego można ograniczyć wyświetlane narzędzia do posiadających określony stan. Przykładowa strona wyświetlana przez ten szablon jest przedstawiona na rysunku 14.3.
Przykład 14.12.
Prosty widok narzędzi
<% template widoknarz1(String stan) %>
<%
if (stan == null) {
narzedzia = pobierzNarzedzia("zyje");
}
else {
narzedzia = pobierzNarzedzia(stan)
}
%>
<% foreach (narzedzie in narzedzia) { %>
<HR SIZE=2 ALIGN=LEFT>
<H3>
<% narzedzie.nazwa %>
<%
if (narzedzie.czasUtworzeniaDni < 45) {
'<FONT COLOR=#FF0000><B> (Nowość!) </B></FONT>'
}
else if (narzedzie.czasModyfkacjiDni < 45) {
'<FONT COLOR=#FF0000><B> (Uaktualnienie!) </B></FONT>'
}
%>
</H3>
<A HREF="<% narzedzie.domURL %>"><% narzedzie.domURL %></A><BR>
<% tool.komentarz %>
<% } %>
Rysunek 14.3.
Lista narzędzi bez dodatków
Jak można dostrzec, na powyższym rysunku nie ma żadnych ozdobników (nagłówka, stopki, paska bocznego) koniecznych na profesjonalnej stronie. Dekoracje te można dodać przez wywołanie innych szablonów, jak przedstawiono w poprawionym szablonie widoknarz2 w przykładzie 14.13.
Przykład 14.13
Bardziej złożony widok narzędzi
<% template widoknarz2(String stan) %>
<%
tytul = "Lista narzędzi"
tytul2 = "Lista narzędzi służących do tworzenia zawartości"
opis = "Bez narzędzi, ludzie nie są niczym więcej niż zwierzętami. I to " &
"dość słabymi. Poniżej przedstawiono listę opartych na serwletach " &
"narzędzi do tworzenia zawartości, które można wykorzystać w celu " &
"wzmocnienia się."
%>
<% call naglowek(tytul, tytul2, opis) %>
<%
if (stan == null) {
narzedzia = pobierzNarzedzia("zyje");
}
else {
narzedzia = pobierzNarzedzia(stan)
}
%>
<% foreach (narzedzie in narzedzia) { %>
<HR SIZE=2 ALIGN=LEFT>
<H3>
<% narzedzie.nazwa %>
<%
if (narzedzie.czasUtworzeniaDni < 45) {
'<FONT COLOR=#FF0000><B> (Nowość!) </B></FONT>'
}
else if (narzedzie.czasModyfkacjiDni < 45) {
'<FONT COLOR=#FF0000><B> (Uaktualnienie!) </B></FONT>'
}
%>
</H3>
<A HREF="<% narzedzie.domURL %>"><% narzedzie.domURL %></A><BR>
<% tool.komentarz %>
<% } %>
<% call stopka() %>
Powyższy nowy szablon definiuje zmienne dla tytułów i opisu strony. Następnie wywołuje szablon nagłówka przekazując mu te wartości. Szablon nagłówka tworzy zawartość nagłówka i paska bocznego, po czym umożliwia widoknarz2 dodanie właściwej zawartości. Na końcu strony szablon stopki dodaje stopkę strony. Przykładowe szablony nagłówka i stopki przedstawione są w przykładach 14.14 i 14.15. Strona wyświetlana przez poprawiony szablon jest przedstawiona na rysunku 14.4.
Przykład 14.14.
Plik nagłówka
<% template naglowek(String tytul, String tytul2, String opis) %>
<HTML><HEAD><TITLE><% tytul %></TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF" BACKGROUND="/obrazki/tlo.gif"
LINK="#003333" ALINK="#669999" VLINK="#333333">
<IMG SRC="/obrazki/banner.gif" WIDTH=600 HEIGHT=87 BORDER=0><BR>
<TABLE>
<TR>
<TD WIDTH=125 VALIGN=TOP>
<BR><BR><BR>
<FONT FACE="Arial,Helvetica" SIZE="+1" COLOR="#FF0000">
<A HREF="/indeks.html">Strona główna</A><BR>
<A HREF="/hosting.html">Hosting</A><BR>
<A HREF="/mechanizmy.html">Mechanizmy</A><BR>
</FONT>
</TD>
<TD WIDTH=475>
<TABLE CELLPADDING=5><TR><TD WIDTH=600 BGCOLOR="#006699" VALIGN=TOP>
<B><FONT FACE="Arial,Helvetica" SIZE="+2">
<% tytul %>
</FONT></B>
</TD></TR></TABLE>
<B><FONT FACE="Arial,Helvetica" SIZE="+1" COLOR="#003366">
<% tytul2 %>
</FONT></B><P>
<P>
<FONT FACE="Arial,Helvetica">
<% opis %>
Przykład 14.15.
Plik stopki
<% template stopka() %>
</FONT>
</TD>
</TR>
<TR>
<TD></TD>
<TD WIDTH=475 ALIGN=CENTER COLSPAN=3>
<HR>
<FONT FACE="Arial,Helvetica">
<A HREF="/indeks.html">Strona główna</A>
<A HREF="/hosting.html">Hosting</A>
<A HREF="/mechanizmy.html">mechanizmy</A> <P>
</FONT>
<TABLE WIDTH=100%>
<TR>
<TD WIDTH=260 ALIGN=LEFT VALIGN=TOP>
<FONT FACE="Arial,Helvetica">
<A HREF="/wlasnosc.html">Wlasność</A> © 2000 Jason Hunter<BR>
Wszystkie prawa zastrzeżone.</TD>
<TD WIDTH=5></FONT></TD>
<TD WIDTH=230 ALIGN=RIGHT VALIGN=TOP>
<FONT FACE="Arial,Helvetica">
Kontakt: <A HREF="mailto:webmaster@servlets.com">webmaster@servlets.com</A>
</FONT></TD>
</TR>
</TABLE>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>
Rysunek 14.4.
Lista narzędzi z otaczającymi dodatkami
Według powyższego projektu zmiany w nagłówku, paku bocznym i stopce są ograniczone do pojedynczych plików. Możliwe jest również wykorzystanie pojedynczego pliku do stworzenia wyglądu całej strony, wstawiając właściwą zawartość przy pomocy polecenia <% ... %>. Podejście to dostarcza większych możliwości i pozwala na oddzielenie właściwej zwartości od formatowania, chociaż trzeba się do tego podejścia przyzwyczaić. Proszę przeczytać dokumentację TeaServlet w celu znalezienia dodatkowych informacji.
Ostatnie słowo
Jedną z najbardziej przydatnych własności Tea trudno opisać w książce — jest to graficzne środowisko programistyczne o nazwie Kettle (imbryk), przedstawione na rysunku 14.5. Oprócz tradycyjnych własności IDE, Kettle dostarcza dokonywanej w czasie rzeczywistym oceny procesu tworzenia — podkreślając na bieżąco wszystkie błędy składni na zielono, błędy semantyczne na czerwono, a błędy kompilacji na niebiesko. Podobna funkcjonalność w narzędziach wspierających inne mechanizmy tworzenia zawartości byłaby wspaniała, ale praktycznie niemożliwa do osiągnięcia. Kettle oferuje również przywoływaną listę właściwości, która pozwala twórcy szablonu na natychmiastowe sprawdzenie, jakie własności są dostępne dla danego obiektu. Kettle jest bezpłatne, ale działa jedynie pod Windows i nie jest to Open Source. Dodatkowo istnieją plany utworzenia reimplementacji Open Source opartej na Java Swing.
Rysunek 14.5.
Herbatę najlepiej robi się w imbryku
Podczas pracy z Tea i TeaServlet można dostrzec brak obsługi specjalnych rodzajów własności, gdyż ich twórcy (WDIG) ich nie potrzebowali. Ponieważ od niedawna jest to Open Source, inni użytkownicy będą dodawać potrzebne im własności i własne pomysły, a produkt będzie rósł. Chociaż język Tea dojrzał, to TeaServlet jest ciągle narzędziem nowym, posiadającym duży potencjał.
Chociaż język Tea został zaprogramowany do formatowania tekstu, właściwie możliwa jest kontrola szablonu Tea nad klasą aplikacji tworzącą rysunek i łatwo jest stworzyć wspólnie dynamiczny obrazek. Inaczej niż pozostałe narzędzia, które zostaną omówione, szablony Tea mogą wyświetlać również dane binarne, tak samo jako znakowe.
Jeżeli klasy aplikacji nie zostały odnalezione nawet jeżeli wygląda na to, że powinny były, powodem może być kwestia związana z mechanizmem ładowania klas. Problemy mogą wystąpić, jeżeli TeaServlet.jar i klasy aplikacji są ładowane przez inne mechanizmy. Należy albo umieścić obie w ścieżce klas systemu, gdzie zostaną odnalezione przez początkowy mechanizm, albo umieścić obie w katalogu WEB-INF, gdzie zostaną odnalezione przez mechanizm ładowania klas aplikacji WWW. (Proszę umieścić TeaServlet.jar w katalogu WEB-INF/lib, a klasy aplikacji w WEB-INF/classes.) Proszę także pamiętać, że z tych samych powodów klasy wspierające nie są ładowane ponownie nawet po wybraniu Reload All (Przeładuj wszystko) ze strony administracyjnej TeaServlet.
Jason Hunter jest jednym z twórców JDOM, razem z Brettem McLaughlinem. Większa ilość informacji na temat JDOM dostępna jest pod adresem http://jdom.org.
2 Część I ♦ Podstawy obsługi systemu WhizBang (Nagłówek strony)
2 H:\Książki\!Wit\Java Servlet Programming\5 do merytorycznej\r14-05.doc