r17-05, Programowanie, ! Java, Java Server Programming


W niniejszym rozdziale:

Rozdział 17.

XMLC

XMLC wykorzystuje technologię XML w celu zmiany plików HTML w zasoby Javy. Został on utworzony przez firmę Lutris jako część serwera Open Source Enhydra Application Server i może zostać pobrany niezależnie, w celu zastosowania go w połączeniu z dowolnym innym kontenerem serwletów. W niniejszym rozdziale omówiony zostanie XMLC 1.2b1 dostępny pod adresem http://xmlc.enhydra.org. XMLC wymaga JDK1.1 lub późniejszego i dowolnej wersji Servlet API. Jest ona udostępniania w licencji Enhydra Public License (EPL), licencji Open Source podobnej do Mozilla Public License (MPL). Mówiąc krótko, według tej licencji zmiany w istniejącym kodzie powinny być przesłane z powrotem do producenta, ale pozwala ona na prywatne, a nawet komercyjne rozszerzenia.

XMLC oznacza XML Compiler (Kompilator XML). Narzędzie to pobiera standardowy dokument HTML lub XML i „kompiluje” go do klasy Javy. Klasa ta zawiera instrukcje Javy potrzebne do utworzenia reprezentacji dokumentu XML DOM (Document Object Model — Model Obiektu Dokumentu) w formie drzewa w pamięci. Programista może manipulować drzewem znajdującym się w pamięci w celu dodania zawartości dynamicznej, a po przetworzeniu może wyświetlić zmodyfikowane drzewo jako HTML, XHTML lub dowolny inny XML.

W tradycyjnym podejściu, projektant strony tworzy jedną lub więcej „makiet” tworzonej strony WWW. Makiety to czyste pliki HTML. Są one przeglądane przez zainteresowane osoby, odbywa się nad nimi dyskusja, po czym, jeżeli to konieczne, zostają one poprawione. Projektant nie musi dodawać do pliku żadnych instrukcji XMLC, jedynie zgodne z HTML 4.0 atrybuty ID do obszarów strony, które mają zostać zastąpione zawartością dynamiczną.

Narzędzie XMLC kompiluje offline makietę strony do klasy Javy zawierającej reprezentację dokumentu w formie drzewa DOM. Programista Javy wykorzystuje publiczny interfejs tej klasy w celu utworzenia klasy manipulacyjnej, która tworzy egzemplarz drzewa, odnajduje części dokumentu, które powinny zostać zmienione i odpowiednio jej modyfikuje. Części te są odnajdywane przy pomocy metod dostępu dodanych do drzewa przez XMLC dla każdego atrybutu ID w dokumencie. W ten sposób pliki HTML stają się właściwie zasobami dla Javy.

Jeżeli na stronie powinna znaleźć się lista elementów, makieta może zawierać pięć lub dziesięć z nich, aby wyglądać realistycznie. Programista Javy może usunąć wszystkie elementy oprócz pierwszego, a następnie skonstruować właściwą listę poprzez powtarzane klonowanie pierwszego elementu — w ten sposób dostosowując się do stylu makiety, ale wykorzystując dane tworzone dynamicznie. Możliwe jest nawet nakazanie kompilatorowi XMLC automatycznego usunięcia wszystkich elementów oprócz pierwszego, co poprawia wydajność.

XMLC osiąga wysoki poziom oddzielenia zawartości i prezentacji. Plik HTML to czysty HTML, pliki Javy to czysta Java, a pliki łączą się jedynie poprzez uzgodnione znaczniki ID. Kiedy utworzone zostają nowe makiety, mogą one zostać wykorzystane bezpośrednio bez konieczności wstecznego dopasowania, a jeżeli ważnego znacznika nie ma w nowej makiecie, wywołany zostaje błąd kompilacji. Dzieje się tak, kiedy klasa manipulacyjna próbuje wywołać metodę odczytującą nieistniejący ID.

Prosta kompilacja XML

Aby nauczyć się stosowania XML należy rozpocząć od prostej aplikacji podstawiającej. Przykład 17.1 przedstawia prostą makietę strony WWW witaj.html, która wita użytkownika po imieniu i podaje informację, ile wiadomości czeka na niego. Tekst, który zostanie podmieniony jest otoczony zgodnymi z HTML 4.0 znacznikami <SPAN>.

Przykład 17.1.

Proste podstawienie XMLC

<!-- witaj.html -->

<HTML>

<HEAD><TITLE>Witaj</TITLE></HEAD>

<BODY>

<H2><SPAN ID="Powitanie">Witaj Agnieszka</SPAN></H2>

Masz <SPAN ID="Wiadomosci">103</SPAN> nowe wiadomości.

</BODY>

</HTML>

W celu uruchomienia XMLC z powyższym plikiem, po pierwsze należy zainstalować XMLC. Należy pobrać plik z wiryny http://xmlc.enhydra.org, rozpakować archiwum i postępować według instrukcji w pliku README mówiących o uruchomieniu xmlc-config. Potem można już uruchomić kompilator xmlc:

xmlc -class Witaj -keep witaj.html

Powyższa linia kodu nakazuje XMLC skompilowanie pliku witaj.html do klasy o nazwie Witaj, zatrzymując wygenerowany plik Witaj.java do sprawdzenia. Ponieważ standardowe pliki HTML nie muszą posiadać właściwej formy XML, XMLC wykorzystuje analizator składniowy Tidy do obsługi konwersji z HTML do DOM. Proszę jednak pamiętać, że Tidy może popełniać błędy podczas obsługi plików HTML zbytnio oddalonych od prawidłowego HTML, a kiedy to nastąpi, podczas kompilacji XMLC wyświetlona zostanie długa lista błędów. Aby wyeliminować ten problem, można wykorzystać narzędzia służące do projektowania HTML (takie jak Dreamweaver) w celu utworzenia HTML nadającego się do przetwarzania XMLC. Większa ilość informacji na temat Tidy jest dostępna pod adresem www.w3.org/People/Raggett/tidy, a na temat portu Javy o nazwie Jtidy pod adresem http://www3.sympatico.ca/ac.quick/jtidy.html.

XMLC obsługuje wiele opcji kompilacyjnych. Najpopularniejsze z nich zostały wymienione poniżej. Składnia linii poleceń wygląda następująco:

xmlc [opcje] plikdok

-class Nazwaklasy

Określa pełną nazwę generowanej klasy. Na przykład Witaj lub com.servlets.xml.Witaj.

-keep

Nakazuje zapamiętanie utworzonego źródła Javy. Przydatne podczas rozpoczynania nauki XMLC.

-methods

Wyświetla podpis każdej tworzonej metody dostępu. Również przydatne przy rozpoczynaniu nauki.

-verbose

Wyświetla dodatkowe informacje na temat procesu kompilacji

-urlmapping staryURL nowyURL

Określa zasadę podmiany URL-i. Wszystkie egzemplarze staryURL zostaną zastąpione przez nowyURL. Pozwala na zmiany w URL-u pomiędzy makietą i produktem. Opcja może zostać podana wielokrotnie.

-urlregexpmapping wyrreg podst

Określa zaawansowaną zasadę podmiany URL-i przy pomocy wyrażeń regularnych. Opcja może zostać podana wielokrotnie.

-urlsetting id nowyURL

Określa URL, który powinien zostać zmodyfikowany w DOM według jego znacznika ID. Url o identyfikatorze id zostanie zmieniony na nowyURL.

-Java prog.

Określa kompilator Javy, który powinien zostać wykorzystany.

-d katalog

Określa docelowy katalog. Przekazywany bezpośrednio kompilatorowi Javy podczas fazy kompilacji.

-delete-class nazwaklasy

Usuwa wszystkie elementy dokumentu posiadające atrybut CLASS nazwaklasy. Przydatne także przy usuwaniu danych makiety, jak zostanie to przedstawione w dalszej części. Opcja może zostać podana wielokrotnie.

-version

Wyświetla wersję XMLC.

Po uruchomieniu XMLC ze znacznikiem - keep można przejrzeć wygenerowane źródło. Źródło Witaj.java jest przedstawione w przykładzie 17.2.

Przykład 17.2.

--> Generowane automatycznie źródło Witaj.java (komentarze przetłumaczone)[Author:F&L]

/*

*******************************************

* KOD WYGENEROWANY PRZEZ XMLC NIE EDYTOWAĆ*

*******************************************

*/

import org.w3c.dom.*;

import org.enhydra.xml.xmlc.XMLCError;

import org.enhydra.xml.xmlc.XMLCUtil;

import org.enhydra.xml.xmlc.dom.XMLCDomFactory;

/**

* klasa dokument XMLC utworzona z pliku

* witaj.html

*/

public class Witaj extends org.enhydra.xml.xmlc.html.HTMLObjectImpl implements org.enhydra.xml.xmlc.XMLObject, org.enhydra.xml.xmlc.html.HTMLObject {

private int $elementId_Powitanie = 8;

private int $elementId_Wiadomosci = 13;

private org.enhydra.xml.lazydom.html.LazyHTMLElement $element_Powitanie;

private org.enhydra.xml.lazydom.html.LazyHTMLElement $element_Wiadomosci;

/**

* Pole wykorzystywane do stwierdzenia, że to jest klasa stworzona przez XMLC

* w łańcuchu dziedziczenia. Zawiera odwołanie do obiektu klasy.

*/

public static final Class XMLC_GENERATED_CLASS = Witaj.class;

/**

* Pole zawierające zależną od CLASSPATH nazwę pliku źródłowego, z którego

* ta klasa została wygenerowana.

*/

public static final String XMLC_SOURCE_FILE = "/witaj.html";

/**

* fabryka XMLC DOM skojarzona z tą klasą.

*/

private static final org.enhydra.xml.xmlc.dom.XMLCDomFactory fDOMFactory = org.enhydra.xml.xmlc.dom.XMLCDomFactoryCache.getFactory(org.enhydra.xml.xmlc.dom.lazydom.LazyHTMLDomFactory.class);;

/**

* Opcje wykorzystywane do preformatowania dokumentu po kompilacji

*/

private static final org.enhydra.xml.io.OutputOptions fPreFormatOutputOptions;

/**

* Szablon wykorzystywany przez wszystkie egzemplarze.

*/

private static final org.enhydra.xml.lazydom.TemplateDOM fTemplateDocument;

/**

* dokument Lazy DOM

*/

private org.enhydra.xml.lazydom.LazyDocument lazyDocument;

/*

* Inicjator klas.

*/

static {

org.enhydra.xml.lazydom.html.LazyHTMLDocument doc = (org.enhydra.xml.lazydom.html.LazyHTMLDocument)fDOMFactory.createDocument(null, "HTML", null);

buildTemplateSubDocument(doc, doc);

fTemplateDocument = new org.enhydra.xml.lazydom.TemplateDOM(doc);

fPreFormatOutputOptions = new org.enhydra.xml.io.OutputOptions();

fPreFormatOutputOptions.setFormat(org.enhydra.xml.io.OutputOptions.FORMAT_AUTO);

fPreFormatOutputOptions.setEncoding("ISO-8859-1");

fPreFormatOutputOptions.setPrettyPrinting(false);

fPreFormatOutputOptions.setIndentSize(4);

fPreFormatOutputOptions.setPreserveSpace(true);

fPreFormatOutputOptions.setOmitXMLHeader(false);

fPreFormatOutputOptions.setOmitDocType(false);

fPreFormatOutputOptions.setOmitEncoding(false);

fPreFormatOutputOptions.setDropHtmlSpanIds(true);

fPreFormatOutputOptions.setOmitAttributeCharEntityRefs(true);

fPreFormatOutputOptions.setPublicId(null);

fPreFormatOutputOptions.setSystemId(null);

fPreFormatOutputOptions.setMIMEType(null);

fPreFormatOutputOptions.markReadOnly();

}

/**

* Domyślny konstruktor.

*/

public Witaj() {

buildDocument();

}

/**

* Konstruktor z opcją tworzenia DOM.

*/

public Witaj(boolean buildDOM) {

if (buildDOM) {

buildDocument();

}

}

/**

* Kopiowanie konstruktora.

*/

public Witaj(Witaj src) {

setDocument((Document)src.getDocument().cloneNode(true), src.getMIMEType(), src.getEncoding());

syncAccessMethods();

}

/**

* Stworzenie dokumentu jako DOM i inicjacja pól metod dostępu.

*/

public void buildDocument() {

lazyDocument = (org.enhydra.xml.lazydom.html.LazyHTMLDocument)((org.enhydra.xml.xmlc.dom.lazydom.LazyDomFactory)fDOMFactory).createDocument(fTemplateDocument);

lazyDocument.setPreFormatOutputOptions(fPreFormatOutputOptions);

setDocument(lazyDocument, "text/html", "ISO-8859-1");

}

/**

* Utworzenie poddrzewa dokumentu.

*/

private static void buildTemplateSubDocument(org.enhydra.xml.lazydom.LazyDocument document,

org.w3c.dom.Node parentNode) {

Node $node0, $node1, $node2, $node3, $node4, $node5;

Element $elem0, $elem1, $elem2, $elem3, $elem4;

Attr $attr0, $attr1, $attr2, $attr3, $attr4;

Element $docElement = document.getDocumentElement();

$node1 = document.createTemplateComment(" witaj.html ", 1);

parentNode.insertBefore($node1, $docElement);

$elem1 = document.getDocumentElement();

((org.enhydra.xml.lazydom.LazyElement)$elem1).makeTemplateNode(2);

((org.enhydra.xml.lazydom.LazyElement)$elem1).setPreFormattedText("<HTML>");

$elem2 = document.createTemplateElement("HEAD", 3, "<HEAD>");

$elem1.appendChild($elem2);

$elem3 = document.createTemplateElement("TITLE", 4, "<TITLE>");

$elem2.appendChild($elem3);

$node4 = document.createTemplateTextNode("Witaj", 5, "Witaj");

$elem3.appendChild($node4);

$elem2 = document.createTemplateElement("BODY", 6, "<BODY>");

$elem1.appendChild($elem2);

$elem3 = document.createTemplateElement("H2", 7, "<H2>");

$elem2.appendChild($elem3);

$elem4 = document.createTemplateElement("SPAN", 8, "<SPAN>");

$elem3.appendChild($elem4);

$attr4 = document.createTemplateAttribute("id", 9);

$elem4.setAttributeNode($attr4);

$node5 = document.createTemplateTextNode("Powitanie", 10, "Powitanie");

$attr4.appendChild($node5);

$node5 = document.createTemplateTextNode("Witaj Agnieszka", 11, "Witaj Agnieszka");

$elem4.appendChild($node5);

$node3 = document.createTemplateTextNode("Masz ", 12, "Masz ");

$elem2.appendChild($node3);

$elem3 = document.createTemplateElement("SPAN", 13, "<SPAN>");

$elem2.appendChild($elem3);

$attr3 = document.createTemplateAttribute("id", 14);

$elem3.setAttributeNode($attr3);

$node4 = document.createTemplateTextNode("Wiadomosci", 15, "Wiadomosci");

$attr3.appendChild($node4);

$node4 = document.createTemplateTextNode("103", 16, "103");

$elem3.appendChild($node4);

$node3 = document.createTemplateTextNode(" nowe wiadomo\u0153ci.", 17, " nowe wiadomo&#339;ci.");

$elem2.appendChild($node3);

}

/**

* Kolonowanie dokumentu.

*/

public Node cloneNode(boolean deep) {

cloneDeepCheck(deep);

return new Witaj(this);

}

/**

* Pobranie fabryki XMLC DOM skojarzonej z klasą.

*/

protected final org.enhydra.xml.xmlc.dom.XMLCDomFactory getDomFactory() {

return fDOMFactory;

}

/**

* Pobranie dokumentu o identyfikatorze <CODE>Powitanie</CODE>.

* proszę zobaczyć org.w3c.dom.html.HTMLElement

*/

public org.w3c.dom.html.HTMLElement getElementPowitanie() {

if (($element_Powitanie == null) && ($elementId_Powitanie >= 0)) {

$element_Powitanie = (org.enhydra.xml.lazydom.html.LazyHTMLElement)lazyDocument.getNodeById($elementId_Powitanie);

}

return $element_Powitanie;

}

/**

* Pobranie dokumentu o identyfikatorze <CODE>Wiadomosci</CODE>.

* proszę zobaczyć org.w3c.dom.html.HTMLElement

*/

public org.w3c.dom.html.HTMLElement getElementWiadomosci() {

if (($element_Wiadomosci == null) && ($elementId_Wiadomosci >= 0)) {

$element_Wiadomosci = (org.enhydra.xml.lazydom.html.LazyHTMLElement)lazyDocument.getNodeById($elementId_Wiadomosci);

}

return $element_Wiadomosci;

}

/**

* Pobranie wartości potomka tekstowego elementu <CODE>Powitanie</CODE>.

* Proszę zobaczyć org.w3c.dom.Text

*/

public void setTextPowitanie(String text) {

if (($element_Powitanie == null) && ($elementId_Powitanie >= 0)) {

$element_Powitanie = (org.enhydra.xml.lazydom.html.LazyHTMLElement)lazyDocument.getNodeById($elementId_Powitanie);

}

doSetText($element_Powitanie, text);

}

/**

* Pobranie wartości potomka tekstowego elementu <CODE>Wiadomosci</CODE>.

* Proszę zobaczyć org.w3c.dom.Text

*/

public void setTextWiadomosci(String text) {

if (($element_Wiadomosci == null) && ($elementId_Wiadomosci >= 0)) {

$element_Wiadomosci = (org.enhydra.xml.lazydom.html.LazyHTMLElement)lazyDocument.getNodeById($elementId_Wiadomosci);

}

doSetText($element_Wiadomosci, text);

}

/**

* Rekursja funkcji ustawiającej pola funkcji dostępu DOM.

* Pola brakujących identyfikatorów ustawione na null.

*/

protected void syncWithDocument(Node node) {

if (node instanceof Element) {

String id = ((Element)node).getAttribute("id");

if (id.length() == 0) {

} else if (id.equals("Powitanie")) {

$elementId_Powitanie = 8;

$element_Powitanie = (org.enhydra.xml.lazydom.html.LazyHTMLElement)node;

} else if (id.equals("Wiadomosci")) {

$elementId_Wiadomosci = 13;

$element_Wiadomosci = (org.enhydra.xml.lazydom.html.LazyHTMLElement)node;

}

}

Node child = node.getFirstChild();

while (child != null) {

syncWithDocument(child);

child = child.getNextSibling();

}

}

}

Powyższy przykład zawiera dużą ilość kodu, nie jest jednak skomplikowany. Klasa nosi nazwę Witaj, jak podano w opcji -class. Jest ona rozszerzeniem org.ehydra.xml.xmlc.html.HTMLObjectImpl, superklasy XMLC dla wszystkich obiektów HTML i implementuje org.w3c.dom.html. --> HTMLObject[Author:F&L] , standardowy interfejs DOM przedstawiający dokument HTML. Konstruktor Witaj wywołuje metodę buildDocument() wypełniającą drzewo DOM zawartością z pliku HTML. DOM jest po prostu zbiorem interfejsów i do wyboru jest wiele jego implementacji. W metodzie createDocument() można dostrzec, że niniejsza klasa wykorzystuje domyślną implementację XMLC, oznaczoną przez DefaultHTMLDomFactory, którą w wersji 1.2b1 jest --> Apache Xerces[Author:F&L] (proszę zobaczyć http://xml.apache.org).

Po uruchomieniu xmlc z opcją -methods można ujrzeć podsumowania wygenerowanych metod dostępu w źródle:

% xmlc -class Witaj -keep -methods witaj.html

public org.w3c.dom.html.HTMLElement getElementPowitanie();

public void setTextPowitanie(String text);

public org.w3c.dom.html.HTMLElement getElementWiadomosci();

public void setTextWiadomosci (String text);

Metody get zwracają obiekt DOM HTMLElement reprezentujący element HTML oznaczony atrybutem ID. Metody set ustawiają zawartość tekstową tych elementów. Proszę zauważyć, że wartość atrybutu ID jest zawarta w nazwach metod. Dla wartości, które nie mogą zostać przekonwertowane na prawidłowe identyfikatory Javy nie zostaną wygenerowane metody dostępu. Tak więc należy być ostrożnym i stosować jedynie litery, cyfry i kreski dolne. Proszę również pamiętać, że wartości identyfikatorów nie powinny powtarzać się na jednej stronie.

Klasa manipulacyjna

Po skompilowaniu pliku HTML do jego opartej na Javie reprezenatcji XML, następnym krokiem jest utworzenie tak zwanej klasy manipulacyjnej, Klasa ta tworzy egzemplarz dokumentu, modyfikuje jego zawartość i wyświetla go we właściwym miejscu. Przykład 17-3 przedstawia pojedynczą klasę manipulacyjną dla strony Witaj. Proszę zauważyć, że jest to samodzielny program. Klasa manipulacyjna powinna być serwletem, lub klasą wywoływaną przez serwlet, ale nie jest to absolutnie konieczne. XMLC nie jest zależny od Servlet API.

Przykład 17.3

Klasa manipulacyjna dla strony Witaj

import java.io.*;

import org.w3c.dom.*;

import org.w3c.dom.html.*;

import org.enhydra.xml.io.DOMFormatter;

public class WitajManipulacja {

public static void main(String[] args) {

// Pewna pseudo-dynamiczna zawartość

String nazwaUzyt = "Jan Kowalski";

int iloscWiadomosci = 43;

// Stworzenie drzewa DOM

Witaj witaj = new Witaj();

// Ustawienie tytułu przy pomocy standardowej metody DOM

witaj.setTitle("Witaj XMLC!");

// Ustawienie wartości dla „powitanie”

witaj.setTextPowitanie("Witaj, " + nazwaUzyt);

// Ustawienie wartości dla „wiadomosci”

witaj.setTextWiadomosci("" + iloscWiadomosci);

try {

DOMFormatter formatuj = new DOMFormatter(); // może być dostosowana

formatuj.write(witaj, System.out);

}

catch (IOException w) {

w.printStackTrace();

}

}

}

Powyższa klasa na początku ustawia pseudodynamiczne wartości, które zostaną dodane do strony — nazwę użytkownika i ilość wiadomości. Następnie tworzy egzemplarz klasy Witaj, która przechowuje zawartość dokumentu HTML. Jeżeli egzemplarz witaj zostałby w tym momencie wyświetlony, ukazałaby się dokładna reprezentacja oryginalnego pliku HTML. Klasa manipulacyjna następnie ustawia tytuł strony, wartość powitania i wartość ilości wiadomości. Powitanie i ilość wiadomości są ustawiane przy pomocy metod dostępu dodanych przez XMLC. Tytuł ustawiany jest przy pomocy standardowej metody DOM. Istnieje wiele ciekawych standardowych metod dostępu dla klas HTML DOM, jak na przykład getApplets(), getImages(), getLinks(), getForms(), getAnchors() i tak dalej. Większa ilość informacji na temat możliwości klas DOM jest zawarta w dokumentacji dołączonej do XMLC. Na końcu klasa wykorzystuje org.enhydra.xml.io.DOMFormatter w celu wyświetlenia egzemplarza Hello w System.out. DOMFormatter może próbować zastosowania w swoim konstruktorze klasy OutputOption w celu sprawdzenia, jak wyświetlany jest XML, ale w przypadku standardowych przeglądarek WWW domyślne zachowanie działa prawidłowo i tworzy:

<HTML><HEAD><TITLE>Witaj XMLC!</TITLE></HEAD><BODY><H2><SPAN id='Powitanie'>Witaj, Jan Kowalski</SPAN></H2>Masz <SPAN id='Wiadomosci'>43</SPAN> nowe wiadomości.</BODY><!-- witaj.html --></HTML>

Proszę zauważyć, że wartości z makiety zostały zastąpione nowymi i usunięta została niepotrzebna wolna przestrzeń w celu poprawy wydajności. Można także dostrzec, że komentarz został umieszczony na końcu pliku. Jest to nieszkodliwy błąd Xerces.

W celu samodzielnego uruchomienia klasy manipulacyjnej należy upewnić się, że plik xmlc.jar zawierający klasy XMLC może zostać odnaleziony w ścieżce klas (lub ścieżce klas serwera, jeżeli XMLC jest wywoływany przez serwlet). Proszę pamiętać, że xmlc.jar zawiera kilka pakietów wspierających, które mogą powodować konflikty, jeżeli zainstalowane zostały także inne wersje tych pakietów. W przypadku XMLC 1.2b1 należy uważać na zewnętrzne kolizje z Apache Xerces 1.0.2, SAX 1.0, DOM Level 2, GNU RegExp 1.0.8 i Jtidy 26jul1999. Wynikiem kolizji pakietów mogą być dziwne komunikaty o błędach. Na przykład, podczas uruchomienia xmlc kompilator może zgłosić, że klasa utworzona przez XMLC jest abstrakcyjna i w związku z tym nie może zostać wykonany jej egzemplarz, ponieważ klasa nie definiuje konkretnej metody z konkretnej klasy. Prawdziwym problemem jest niedopasowanie dwóch wersji DOM. Rozwiązaniem jest upewnienie się że najnowsza wersja występuje wcześniej w ścieżce klas. Rozwiązanie to działa, dopóki wersje pakietów są wstecznie kompatybilne.

Modyfikacja listy

Teraz zostanie opisana nieco bardziej zaawansowana operacja — modyfikacja listy. Po pierwsze utworzona zostanie strona prototypowa zawierająca makietę listy, jak przedstawiono w przykładzie 17.4.

Przykład 17.4.

Makieta listy języków

<!-- przegl.html -->

<HTML><HEAD><TITLE>Przeglądanie XMLC </TITLE></HEAD>

<H1>Lokalizacje klienta</H1>

<UL>

<LI ID="lokal">en

<LI CLASS="makieta">es

<LI CLASS="makieta">jp

</UL>

</BODY></HTML>

W rzeczywistości powyższa lista byłaby częścią dużo większej strony, ale dla XMLC nie jest to w ogóle interesujące. Strona zostaje skompilowana przy pomocy kompilatora XMLC, z opcją -delete-class makieta w celu automatycznego usunięcia w fazie kompilacji elementów oznaczonych jako makieta:

% xmlc -class Przegl -keep -delete-class makieta -methods przegl.html

public org.w3c.dom.html.HTMLLIElement getElementLokal();

Proszę zauważyć, że do dokumentu została dodana tylko jedna metoda dostępu — getElementLokal(), która zwraca element <ID> oznaczony jako lokal w obiekcie HTMLLIElement. Klasa manipulacyjna dynamicznie zmieniająca powyższy dokument jest przedstawiona w przykładzie 17.5.

Przykład 17.5.

Klasa manipulacyjna dla listy języków

import java.io.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

import org.w3c.dom.*;

import org.w3c.dom.html.*;

import org.enhydra.xml.io.DOMFormatter;

public class PrzeglManipulacja extends HttpServlet {

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

odp.setContentType("text/html");

PrintWriter wyj = odp.getWriter();

// Pobranie do wyświetlenia pewnych danych dynamicznych

Enumeration lokale = zad.getLocales();

// Utworzenie drzewa DOM

Przegl przegl = new Przegl();

// Pobranie pierwszego, „prototypowego” elementu listy

// Reszta została usunięta podczas kompilacji xmlc

HTMLLIElement element = przegl.getElementLokal();

// Pobranie przodka prototypu w celu umożliwienia zarządzania potomkami

Node przodek = element.getParentNode();

// Pętla nad lokalizacjami i dodanie węzła dla każdej

while (lokale.hasMoreElements()) {

Locale lok = (Locale)lokale.nextElement();

HTMLLIElement nowyElement = (HTMLLIElement) element.cloneNode(true);

Text tekst = przegl.createTextNode(lok.toString());

nowyElement.replaceChild(tekst, newItem.getLastChild());

przodek.insertBefore(nowyElement, null);

}

// Usunięcie elementu prototypowego

przodek.removeChild(element);

// Wyświetlenie dokumentu

DOMFormatter formatuj = new DOMFormatter(); // może być poprawiony

formatuj.write(przegl, out);

}

}

Interesującą częścią powyższego serwletu jest pętla while. Dla każdej lokalizacji serwlet klonuje element prototypowy, zmienia tekst przechowywany przez klon i wstawia klon na końcu listy. Po pętli while serwlet usuwa element prototypowy, pozostawiając listę pełną klonów zawierającą prawdziwe dane. Wynik przedstawiono na rysunku 17.1.

0x01 graphic

Rysunek 17.1. Skróty lokalizacji — angielski (USA), wietnamski i tajski

W powyższym przykładzie można dostrzec, że jedną z trudności XMLC jest obsługa nieintuicyjnego modelu obiektów DOM. Ze stosowaniem DOM związane są pewne sztuczki. Na przykład, można dostrzec, że zawartość String może zostać utworzona jedynie przy pomocy metody fabryki createTextNode() wywoływanej na dokumencie, do którego ma zostać dodany tekst, a kiedy zawartość jest już gotowa, model dodawania tekstu do dokumentu wymaga dodania go jako węzła potomnego swojego elementu i usunięcia istniejącego potomka. Rozważa się możliwość dołączenia dużo łatwiejszego w obsłudze modelu obiektów JDOM do przyszłych wersji XMLC. Poza tym, pakiety narzędziowe dla XMLC przejmują DOM i odchodzą od niektórych z bardziej nieprzyjemnych jego wymagań.

Aplikacja „Narzędzia”

W celu zakończenia dyskusji nad XMLC opisany zostanie sposób tworzenia przy pomocy XMLC aplikacji „Narzędzia” wykorzystanej wcześniej do zademonstrowania Tea i WebMacro. Na początku opisany zostanie szablon HTML przedstawiający wspólny projekt graficzny witryny, ale nie posiadający żadnej zawartości odwołującej się do listy narzędzi. Jest on przedstawiony w przykładzie 17.6.

Przykład 17.6

Plik szablonu aplikacji „Narzędzia”

<!-- szablon.html -->

<HTML><HEAD><TITLE>Przykładowy Tytuł</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">

<SPAN ID="tytul">Przykładowy tytuł</SPAN>

</FONT></B>

</TD></TR></TABLE>

<B><FONT FACE="Arial,Helvetica" SIZE="+1" COLOR="#003366">

<SPAN ID="tytul2">Przykładowy drugi tytuł</SPAN>

</FONT></B><P>

<P>

<FONT FACE="Arial,Helvetica">

<SPAN ID="opis">Przykładowy opis</SPAN>

<DIV id="zawartosc">Tu właściwa zawartość</DIV>

</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>&nbsp;&nbsp;

<A HREF="/hosting.html">Hosting</A>&nbsp;&nbsp;

<A HREF="/mechanizmy.html">Mechanizmy</A>&nbsp;&nbsp;<P>

</FONT>

<TABLE WIDTH=100%>

<TR>

<TD WIDTH=260 ALIGN=LEFT VALIGN=TOP>

<FONT FACE="Arial,Helvetica">

<A HREF="/wlasnosc.html">Własność</A> &copy; 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>

Proszę zauważyć, że tytuł strony to obszar zablokowany oraz że występują trzy elementy <SPAN> o nazwach tytul, tytul2 i opis zawierające makietowy tekst, który zostanie wymieniony. Występuje także element <DIV> o nazwie zawartosc, oznaczający miejsce, w którym powinna zostać wyświetlona właściwa zawartość strony. Plik HTML, który zostanie wykorzystany do wygenerowania zawartości aplikacji „Narzędzia” jest przedstawiony w przykładzie 17.7.

Przykład 17.7.

Plik zawartości aplikacji „Narzędzia”

<!-- widoknarz.html -->

<HTML><HEAD><TITLE>Lista narzędzi</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">

<SPAN ID="tytul">Lista narzędzi</SPAN>

</FONT></B>

</TD></TR></TABLE>

<B><FONT FACE="Arial,Helvetica" SIZE="+1" COLOR="#003366">

<SPAN ID="tytul2">Lista narzędzi do tworzenia zawartości</SPAN>

</FONT></B><P>

<P>

<FONT FACE="Arial,Helvetica">

<SPAN ID="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ę."

</SPAN>

<DIV ID="rekord">

<HR SIZE=2 ALIGN=LEFT>

<FONT FACE="Arial,Helvetica">

<H3>

<SPAN ID="nazwaNarz">Jakaś nazwa narzędzia</SPAN>

<FONT COLOR=#FF0000><B> <SPAN ID="stanNarz">(Nowość!)</SPAN> </B></FONT>

</H3>

<A ID="laczeNarz" HREF="http://narzedzia.com">http://narzedzia.com</A><BR>

<SPAN ID="komentarzNarz">

Tutaj komentarz na temat narzędzia.

</SPAN>

</FONT>

</DIV>

<DIV>

<HR SIZE=2 ALIGN=LEFT>

<FONT FACE="Arial,Helvetica">

<H3>

Inna nazwa narzędzia

<FONT COLOR=#FF0000><B> (Uaktualnienie!) </B></FONT>

</H3>

<A HREF="http://narzedzia.com">http://narzedzia.com</A><BR>

Tu komentarz na temat tego narzędzia.

</FONT>

</DIV>

</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>&nbsp;&nbsp;

<A HREF="/hosting.html">Hosting</A>&nbsp;&nbsp;

<A HREF="/mechanizmy.html">Mechanizmy</A>&nbsp;&nbsp;<P>

</FONT>

<TABLE WIDTH=100%>

<TR>

<TD WIDTH=260 ALIGN=LEFT VALIGN=TOP>

<FONT FACE="Arial,Helvetica">

<A HREF="/wlasnosc.html">Własność</A> &copy; 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>

Powyższy plik wygląda bardzo podobnie do szablonu, ale wartości obszarów zablokowanych zostały wymienione na realistyczne i wypełniono obszar zawartości pewnymi prototypowymi rekordami. Oto sposób wykorzystania tego pliku: programowo pobrane zostaną jego kluczowe elementy (tytuły, opis i prototypowy rekord), po czym zostaną one skopiowane i umieszczone w pliku szablonu w celu utworzenia ostatecznej wersji strony.

Po co wykorzystywać dwa pliki? Czy nie jest możliwe po prostu bezpośrednie zmodyfikowanie pliku widoknarz.html? Jest to możliwe, ale szablon wykorzystywany jest po to, by w przypadku przyszłego uaktualnienia nagłówka, paska bocznego lub stopki konieczne było uaktualnienie jedynie pliku szablon.html i zmiana ta została uwidoczniona na wszystkich stronach. Innymi słowy, szablon narzuca ogólny wygląd strony. Plik widoknarz.html jest wykorzystywany jedynie w kluczowych częściach.

Pliki HTML są poddawane kompilacji XMLC przy pomocy poniższych poleceń. Mogą wystąpić ostrzeżenia, ponieważ HTML w nich zawarty nie jest prawidłowym XML:

% xmlc -class Szablon -keep -methods szablon.html

% xmlc -class WidokNarz -keep -methods widoknarz.html

Następnie można utworzyć serwlet który działa jako klasa manipulacyjna. Jest on przedstawiony w przykładzie 17.8.

Przykład 17.8.

Klasa manipulacyjna aplikacji „Narzędzia”

import java.io.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

import org.w3c.dom.*;

import org.w3c.dom.html.*;

import org.enhydra.xml.io.DOMFormatter;

public class WidokNarzSerwlet extends HttpServlet {

private Narzedzie[] narzedzia;

public void init() throws ServletException {

// Załadowanie danych narzędzi w init w celu uproszczenia

String plikNarz = getInitParameter("plikNarz"); // z web.xml

if (plikNarz == null) {

throw new ServletException(

"Plik danych narzędzi musi być określony jako parametr inicjacji plikNarz ");

}

log("Ładowanie narzędzi z " + plikNarz);

try {

narzedzia = Narzedzie.ladujNarzedzia(plikNarz);

if (narzedzia.length == 0) {

log("Nie odnaleziono narzędzi w " + plikNarz);

}

else {

log(narzedzia.length + " narzędzi znaleziono w " + plikNarz);

}

}

catch (Exception w) {

throw new ServletException(w);

}

}

public void doGet(HttpServletRequest zad, HttpServletResponse odp)

throws ServletException, IOException {

odp.setContentType("text/html");

PrintWriter wyj = odp.getWriter();

// Utworzenie drzewa DOM dla pełnego dokumentu

Szablon szablon = new Szablon();

// Utworzenie drzewa DOM przechowującego wewnętrzną zawartość

WidokNarz widoknarz = new widokNarz();

// Pobranie prototypowego rekordu narzędzia

HTMLDivElement rekord = widoknarz.getElementRekord();

// Pobranie odwołania do punktu wstawiania dla listy narzędzi

HTMLDivElement punktWstaw = szablon.getElementZawartosc();

Node przodekWstaw = punktWstaw.getParentNode();

// Ustawienie tytułów i opisu

// Usunięcie danych z pliku widoknarz.html

String tytul = ((Text)widoknarz.getElementTytul().getFirstChild()).getData();

String ttul2 = ((Text)widoknarz.getElementTytul2().getFirstChild()).getData();

String opis = ((Text)widoknarz.getElementOpis().getFirstChild()).getData();

szablon.setTitle(tytul); // tytuł strony

szablon.setTextTytul(tytul); // element oznaczony „tytul”

template.setTextTytul2(tytul2); // element oznaczony „tytul2”

template.setTextOpis(opis); // element oznaczony „opis”

// Pętla nad narzędziami, dodanie nowego rekordu dla każdego

for (int i = 0; i < narzedzia.length; i++) {

Narzedzie narzedzie = narzedzia[i];

widoknarz.setTextNazwaNarz(narzedzie.nazwa);

widoknarz.setTextToolKomentarz(narzedzie.komentarz);

if (narzedzie.czyNowy(45)) {

widoknarz.setTextToolStan(" (Nowość!) ");

}

else if (narzedzie.czyUaktualniony(45)) {

widoknarz.setTextToolStan(" (Uaktualnienie!) ");

}

else {

widoknarz.setTextToolStan("");

}

HTMLAnchorElement lacze = toolview.getElementLaczeNarz();

lacze.setHref(narzedzie.domURL);

Text laczeTekst = widoknarz.createTextNode(narzedzie.domURL);

lacze.replaceChild(laczeTekst, lacze.getLastChild());

// importNode() to DOM Level 2

przodekWstaw.insertBefore(szablon.importNode(rekord, true), null);

}

// Usunięcie obszaru zablokowanego

przodekWstaw.removeChild(punktWstaw);

// Wyświetlenie dokumentu

DOMFormatter formatuj = new DOMFormatter(); // może być poprawiony

formatuj.write(szablon, out);

}

}

Metoda init() serwletu pobiera dane narzędzi z pliku określonego w parametrze inicjacji plikNarz, przy pomocy klasy Narzedzie z rozdziału 14, „Szkielet Tea”. Ciekawe działanie następuje w metodzie doGet(). Tworzone są egzemplarze dokumentów Szablon i WidokNarz. Następnie odnajdywany jest prototypowy rekord narzędzia w WidokNarz oraz punkt wstawiania dla rekordów narzędzie w Szablon. Następnie odczytywane są tytuły i opis z dokumentu WidokNarz, po czym wartości te zostają skopiowane do Szablon.

W pętli for dokonywana jest obsługa zadania tworzenia listy rekordów narzędzi. Dla każdego narzędzia do prototypowego rekordu przypisane zostają odpowiednie wartości — na początku nazwa i komentarz, po czym łącze. Po zmodyfikowaniu rekordu zostaje on dodany do szablonu w punkcie wstawiania. W DOM węzły przypisane są do dokumentu, który je utworzył, tak więc zastosowana zostaje metoda importNode() w celu udostępnienia rekordu przesuwanego z dokumentu WidokNarz do Szablon. Metoda importNode() wykonuje głębokie kopiowanie (ponieważ przekazano jej jako drugi argument true), tak więc każda iteracja pętli for dodaje inna kopię zawartości rekordu. Po zakończeniu pętli for punkt wstawiania zostaje usunięty z szablonu i dokument jest wyświetlany klientowi.

Podczas uruchamiania powyższego serwletu należy pamiętać, że metoda importNode() jest nowością w DOM Level 2, tak więc, aby mógł on zostać uruchomiony, serwer musi posiadać ścieżkę klas zawierającą DOM Level 2 przed wszystkimi klasami DOM Level 1. Archiwum xmlc.jar zawiera DOM Level 2, tak więc można pomyśleć, że nie ma żadnego problemu, ale niektóre serwery (włączając w to Tomcat 3.2) posiadają w swojej domyślnej ścieżce implementację DOM Level 1 pomagającą w odczytywaniu plików web.xml. Aby powyższy serwlet mógł pracować na takich serwerach konieczna jest edycja ścieżki klas serwera w celu upewnienia się, że plik XMLC xmlc.jar znajduje się przed własnymi bibliotekami XML serwera. (Tomcat 3.2 automatycznie ładuje pliki JAR w porządku alfabetycznym, tak więc konieczna może okazać się zmiana nazw plików JAR.)

Jednym z problemów związanych z XMLC jest przedstawiony przez powyższy przykład fakt, że warunkowo dołączane bloki tekstu, których zawartość może się zmieniać, podobnie jak uwagi (Nowość! i Uaktualnienie!) mogą sprawiać trudności przy dołączaniu do strony, ponieważ szablon może zadeklarować tylko jeden możliwy blok tekstu do dołączenia. W powyższym przykładzie oczywiste jest, że kod Javy wie więcej niż powinien na temat tworzenia uwag.

Miłą własnością XMLC jest automatyczne umieszczanie kodów ucieczkowych przed znakami specjalnymi takimi jak <, > i & podczas wyświetlania ich do klienta, ponieważ wtedy narzędzie dokonujące wyświetlenia może bez problemu rozpoznać, co jest strukturą, a co zawartością. Ta wiedza o strukturze dokumentu umożliwia również mechanizmowi formatującemu na wykonywanie zaawansowanych zadań takich jak modyfikacja wszystkich łączy w celu zakodowania identyfikatorów sesji, chociaż ta konkretna własność nie została jeszcze wprowadzona.

Strona wygenerowana przez powyższy serwlet wygląda identycznie jak strona utworzona przez przykłady Tea, albo WebMacro.

Enhydra Lutris to naukowa nazwa morskiej wydry, co jest odpowiednim identyfikatorem dla firmy z siedzibą w Santa Cruz. W pobliżu tego miasta znajduje się zatoka Monterey, w której żyje mnóstwo wydr.

XMLC jest dostępny również jako zadanie Ant, co może zainteresować osoby wykorzystujące doskonałe narzędzie kompilacji ANT. Proszę zobaczyć http://jakarta.apache.org.

Proszę pamiętać o interakcjach w ścieżce klas podczas uruchamiania powyższego przykładu. Wykorzystywane są biblioteki potrzebne ZMLC, JDOM i przypuszczalnie samemu serwerowi. Usatysfakcjonowanie wymagań biblioteki wersji XML dla wszystkich trzech składników bez konfliktów może wymagać sporych umiejętności w czarnej magii i nie zawsze jest możliwe.

2 Część I Podstawy obsługi systemu WhizBang (Nagłówek strony)

2 C:\0-praca\Java Servlet - programowanie. Wyd. 2\r17-rysunki.doc

Plik utworzony przez XMLC 2.0.1. Wersja 1.2b1 nie jest już dostępna w sieci. Występują zmiany

Zmiana wersji

W wersjach późniejszych Lazy DOM, modyfikacja Apache Xerces



Wyszukiwarka

Podobne podstrony:
r12-05, Programowanie, ! Java, Java Server Programming
r20-05, Programowanie, ! Java, Java Server Programming
O Autorach-05, Programowanie, ! Java, Java Server Programming
r05-05, Programowanie, ! Java, Java Server Programming
r07-05, Programowanie, ! Java, Java Server Programming
r03-05, Programowanie, ! Java, Java Server Programming
rE-05, Programowanie, ! Java, Java Server Programming
r19-05, Programowanie, ! Java, Java Server Programming
r11-05, Programowanie, ! Java, Java Server Programming
rD-05, Programowanie, ! Java, Java Server Programming
rF-05, Programowanie, ! Java, Java Server Programming
r04-05, Programowanie, ! Java, Java Server Programming
r13-05, Programowanie, ! Java, Java Server Programming
r01-05, Programowanie, ! Java, Java Server Programming
r10-05, Programowanie, ! Java, Java Server Programming
r02-05, Programowanie, ! Java, Java Server Programming
rB-05, Programowanie, ! Java, Java Server Programming
r12-05, Programowanie, ! Java, Java Server Programming
05 Programowanie

więcej podobnych podstron