Hinweis:
Aus Platzgründen wurden in diesem Artikel die umfangreichen Quellcodes nur auszugsweise abgepictureet. Sämtliche Quellcodes, auf die sich der Autor bezieht, befinden sich aber auf der Heft-CD des Java-Magazins 5.2002. Zum Nachvollziehen der Beispiele empfehlen wir, die Quellcodes vollständig auszudrucken.JDOM
Einer der Vorteile der Standard-APIs in XML ist deren sprachenübergreifende Verfügbarkeit. Vielleicht fällt Ihnen bei den Beispielen der vorigen Folgen dieser Serie auf, dass die meisten Bibliothek-importe mit org.w3c.* oder org.xml.* beginnen. SAX und DOM sind Standard-APIs, die vom World Wide Web Consortium (W3C) und anderen Standardisierungsorganen entwickelt wurden. Das ist besonders angenehm, wenn Sie Code in mehr als einer Sprache schreiben, der XML verarbeiten muss - Sie können dann dieselbe API in Java verwenden, die Sie auch in C++ oder Perl benutzen. Da sie von einem Standardisierungsorgan definiert werden, sind sie über alle unterstützte Sprachen konsistent. Einer der größten Nachteile der Standard-APIs ist jedoch deren sprachenübergreifende Verfügbarkeit! Das Problem dabei besteht darin, dass jede der APIs ihre gesamten Datenstrukturen von Grundprinzipien aus definieren muss. Anders ausgedrückt, das Standardisierungsorgan kann aus keinen Standardmerkmalen der Sprache Vorteile ziehen, da sie sie auf diesen sprachenübergreifenden Standard überschreiben. Dies zeigt sich besonders deutlich im DOM-API für Komponentensammlungen. DOM definiert Datenstrukturen wie NamedNodeMap für die Handhabung von Sammlungen. Wenn Sie Java verwenden, wäre es natürlich angenehm, wenn Sie die Java-Standardsammlungen (wie Maps, Lists usw.) verwenden könnten. Jedoch kann die DOM-API nicht auf diese Merkmale der Java-Sprache überschrieben werden. Hier ist eine Reihe von Klassen erforderlich, die die Standard-APIs unterstützen, aber auch Java-Standardkonstruktionen nutzen. Genau darum geht es bei JDOM. JDOM ist kein Akronym (genauso wie JDBC technisch kein Akronym ist); es ist eine Open-Source-Bibliothek mit XML-Verarbeitungscode, der die XML-Standard-APIs unterstützt, aber Java-spezifisch. In anderen Worten, Sie können SAX und DOM nur mit Java-Standardsammlungen verwenden. JDOM ist über die Internetadresse www.jdom.org erhältlich. Es ist ein Projekt mit offenem Quellcode, das beim Abfassen dieses Artikels als Beta-Version vorlag. Die Autoren versuchen damit, eine Alternative mit völlig offenem Quellcode zu den Standard-APIs für Java-Entwicker zu schaffen. JDOM verwendet die gleiche Lizenz für offenen Quellcode wie der Apache-Webserver (das bedeutet, jeder kann es verwenden, ohne auch sein Produkt zu offenem Quellcode zu machen). Es unterstützt SAX, DOM, Validierungen, XSLT-Konvertierungen und weitere XML-Merkmale.Listing 1
<?xml version="1.0" encoding="UTF-8"?><Configuration><Position left="100" top="100"/><Visuals title="default title"/></Configuration>
package xmlconfig;import java.io.*;import java.util.*;import java.net.*;import org.w3c.dom.*;import org.apache.xerces.dom.DocumentImpl;import org.apache.xerces.dom.DOMImplementationImpl;import org.w3c.dom.Document;import org.apache.xml.serialize.OutputFormat;import org.apache.xml.serialize.Serializer;import org.apache.xml.serialize.SerializerFactory;import org.apache.xml.serialize.XMLSerializer;import org.apache.xerces.parsers.DOMParser;public class Configuration {private Configuration() throws NoConfigFileException {//-- load configuration infoloadConfig();}static public Configuration getConfiguration()throws NoConfigFileException {if (myConfig == null) {myConfig = new Configuration();}return(myConfig);}private void loadConfig() throws NoConfigFileException {System.out.print("Parsing XML File: " +CONFIG_FILE_NAME + "...");DOMParser parser = new DOMParser();try {parser.parse(CONFIG_FILE_URI);Document doc = parser.getDocument();recurseDOMTree(doc);} catch (Exception e) {e.printStackTrace();System.out.println("Error in parsing: " +e.getMessage());}System.out.println("Done.");}public void recurseDOMTree(Node node) {switch (node.getNodeType()) {case Node.ELEMENT_NODE:String name = node.getNodeName();NamedNodeMap attributes = node.getAttributes();if (name.equalsIgnoreCase("position")) {top = Integer.parseInt(attributes.getNamedItem("top").getNodeValue());left = Integer.parseInt(attributes.getNamedItem("left").getNodeValue());} else if (name.equalsIgnoreCase("visuals")) {title = attributes.getNamedItem("title").getNodeValue();} //elsebreak;} //switch//-- recurse on each childNodeList children = node.getChildNodes();if (children != null) {for (int i=0; i<children.getLength(); i++) {recurseDOMTree(children.item(i));} //for} //if}public static void main(String[] args) {System.out.print("Building default configuration file at "+ CONFIG_FILE_NAME + "...");Document doc = new DocumentImpl();Element root = doc.createElement("Configuration");Element posElem = doc.createElement("Position");posElem.setAttribute("left", "100");posElem.setAttribute("top", "100");root.appendChild(posElem);Element titleElem = doc.createElement("Visuals");titleElem.setAttribute("title", "default title");root.appendChild(titleElem);doc.appendChild(root);OutputFormat format = new OutputFormat(doc);StringWriter stringOut = new StringWriter();XMLSerializer serial = new XMLSerializer(stringOut, format);FileWriter fw = null;try {serial.asDOMSerializer();serial.serialize(doc.getDocumentElement());fw = new FileWriter(CONFIG_FILE_NAME);fw.write(stringOut.toString());} catch (IOException ioex) {ioex.printStackTrace();} finally {try {fw.flush();fw.close();} catch (IOException ignored) {} //catch} //finallySystem.out.println("Done.");}public void setTop(int newTop) {top = newTop;}public int getTop() {return(top);}public void setLeft(int newLeft) {left = newLeft;}public int getLeft() {return(left);}public void setTitle(String newTitle) {title = newTitle;}public String getTitle() {return(title);}static private Configuration myConfig;static public final String CONFIG_FILE_NAME ="c:/data/dom_config.xml";static private final String CONFIG_FILE_URI ="file://localhost/" + CONFIG_FILE_NAME;private static final String DEFAULT_DOM_ADAPTER_CLASS ="org.jdom.adapters.XercesDOMAdapter";private int top;private int left;private String title;}class NoConfigFileException extends IOException {NoConfigFileException() {super();}NoConfigFileException(String msg) {super(msg);}}
Listing 3
. . .public static void main(String[] args) {System.out.print("Building default configuration file at "+ CONFIG_FILE_NAME + "...");DOMBuilder builder = new DOMBuilder<br>(DEFAULT_DOM_ADAPTER_CLASS);Element root = new Element("Configuration");Element posElem = new Element("Position");posElem.addAttribute("left", "100");posElem.addAttribute("top", "100");root.addContent(posElem);Element titleElem = new Element("Visuals");titleElem.addAttribute("Title", "Default Title");root.addContent(titleElem);Document doc = new Document(root);FileOutputStream fos = null;try {fos = new FileOutputStream(CONFIG_FILE_NAME);XMLOutputter outputter = new XMLOutputter();outputter.output(doc, fos);} catch (IOException ioe) {ioe.printStackTrace();} finally {try {fos.flush();fos.close();} catch (IOException ignored) {} //catch} //finallySystem.out.println("done.");}. . .private void loadConfig()throws NoConfigFileException, JDOMException {DOMBuilder builder = new DOMBuilder<br>(DEFAULT_DOM_ADAPTER_CLASS);FileInputStream in = null;try {in = new FileInputStream(CONFIG_FILE_NAME);//build a DOM tree with the specified or default parserDOMAdapter domAdapter;Class parserClass = Class.forName<br>(DEFAULT_DOM_ADAPTER_CLASS);domAdapter = (DOMAdapter)parserClass.newInstance();//-- Build the JDOM Documentorg.w3c.dom.Document w3cdoc =domAdapter.getDocument((InputStream)in, false);Document doc = builder.build(w3cdoc);//-- Print out entire set of attributesElement elem = doc.getRootElement();java.util.List lst = elem.getChildren();Iterator it = lst.iterator();while (it.hasNext()) {Element e = (Element) it.next();java.util.List child = e.getAttributes();Iterator i = child.iterator();while (i.hasNext()) {Attribute attr = (Attribute) i.next();System.err.println(attr.getName() + " : " +attr.getValue() );}}//-- get config info for propertiestop = Integer.parseInt(elem.getChild("Position").getAttribute("top").getValue());left = Integer.parseInt(elem.getChild("Position").getAttribute("left").getValue());title = elem.getChild("Visuals").getAttribute("Title").getValue();} catch (ClassNotFoundException e) {throw new JDOMException("Parser class " +<br>DEFAULT_DOM_ADAPTER_CLASS + <br>" not found");} catch (IOException ioe) {ioe.printStackTrace();} catch (Exception e) {throw new JDOMException("Parser class " + DEFAULT_DOM_ADAPTER_CLASS +" instantiation error");} finally {try {in.close();} catch (IOException ignored) {} //catch}}
elem.getChild("Position").getAttribute("top").getValue());
SOAP
SOAP steht für Simple Object Access Protocol. Es ist eine Methode mit offenem Standard zur Ausführung von Fernprozeduraufrufen, die ein textbasiertes Transportmittel (normalerweise HTTP) und XML verwendet. Es ist eine der Grundlagen von Web Services, was derzeit bei verteilten Rechnersystemen ein topaktuelles Thema ist. Doch warum ist SOAP so wichtig und warum sind alle so begeistert von dieser Art, Methodenaufrufe durchzuführen? Letztlich steht es für einen einfachen Weg, sprachenübergreifend ferngesteuerte Prozeduren aufzurufen. Das soll nicht heißen, dass dies lange Zeit nicht möglich gewesen wäre. Schließlich geht es bei COM, CORBA und RMI genau darum. Das Problem bei diesen Methodenaufrufverfahren liegt jedoch darin, dass es sich bei allen um binäre Standards handelt. Dies funktioniert einfach und effizient in lokalen Netzwerken, wird aber problematisch, wenn man es bei verteilten Rechnersystemen im Internet versucht. Fragen Sie doch mal, wie viele Netzwerkadministratoren binäre Ports an den Firewalls Ihrer Firma öffnen wollen! SOAP und Internetdienste sind attraktiv, da die meisten Netzwerke den freien Durchgang von Text bereits zulassen (normalerweise über den HTTP-Port). SOAP steht für eine sprachen- und plattformunabhängige Technik zur Ausführung von Methodenaufrufen, indem die Daten innerhalb von XML geordnet und dieses XML-Paket dann über HTTP transportiert wird. Ich möchte beschreiben, wie man die SOAP-Implementierung von Apache einsetzt. Es ist keine Einführung zu SOAP an sich (dies allein würde mindestens ein Buch füllen), doch ich werde SOAP gerade so ausführlich behandeln, dass die Beispiele verständlich werden. Beachten Sie, dass Sie SOAP ohne irgendeine Systemumgebung programmieren können - wenn Sie einen XML-Parser haben, können Sie mit SOAP arbeiten. Es ist jedoch immer angenehm, wenn schon ein anderer (wie Apache) den meisten Installationscode für Sie geschrieben hat. Das SOAP Toolkit von Apache ermöglicht es Ihnen, entweder SOAP-Dienste anzulegen oder Clients, die mit SOAP-Diensten kommunizieren. Wir werden beides besprechen. Doch zunächst müssen Sie das System installieren.Installation
Die Installation von SOAP läuft ähnlich wie die Installation von anderen Apache-Produkten. Sie laden eine Zip-Datei herunter, die in ein Verzeichnis entpackt werden muss. Jetzt sind nur noch einige wenige Schritte zu tun, dann ist SOAP installiert und startbereit. Diese werden in der Dokumentation behandelt, die mit der SOAP-Implementierung von Apache geliefert wird; ich erwähne sie hier deshalb nur kurz. Es gibt separate Anweisungen für die Installationen der Client-Seite zur Server-Seite, doch der Server ist dem Client übergeordnet. Für den Client müssen Sie zuerst Folgendes besorgen:- den Klassenpfad aktualisieren, um die Datei soap.jar einzubinden
- mail.jar, von der Sun-Internetadresse (java.sun.com/products/javamail/)
- activation.jar, ein Bestandteil des JavaBeans Activation Framework (java.sun.com/products/beans/glasgow/jaf.html)
- einen beliebigen JAXP-kompatiblen XML-Parser (ich selbst habe Xerces 1.4.0 eingesetzt). Wenn Sie einen älteren Parser (der Namespaces nicht unterstützt) in Ihrem Klassenpfad haben, müssen Sie sicherstellen, dass der neuere Parser im Klassenpfad vor dem älteren erscheint.
Sorry, I don't speak via HTTP GET- you have to use HTTP POST to talk to me.
SOAP-Dienste
Ein SOAP-Dienst ist ein Software-Artefakt, das Abfragen empfängt und durch das Senden von SOAP-Meldungen antwortet, die normalerweise eine Nutzinformation enthalten. Das meiste SOAP-spezifische XML dient als Leitwegangaben und Metadaten für das Thema der Nachricht (oder Nutzlast). Sie können sich SOAP als einen Umschlag vorstellen, der das Dokument von irgendeinem Absender per Post zu Ihnen nach Hause trägt. SOAP ist eigentlich nur ein Codierungsmechanismus, der es Ihnen ermöglicht, Methoden, Parameter und Rückmeldungsarten anzugeben, die nicht an eine bestimmte Sprache gebunden sind. Wenn Sie einen Dienst anlegen, müssen Sie entscheiden, welche Dienste Sie anbieten wollen. Eines der Beispiele, die mit SOAP von Apache ausgeliefert werden, ist ein Adressbuchdienst. Er ermöglicht es Ihnen, eine Adresse zu einem Namen abzufragen, neue Adressen hinzuzufügen, das vorhandene Adressbuch anzuzeigen und Listen hinzuzufügen. Die Implementierung dieses Dienstes speichert die Adressen in Hashtable, doch für den Abnehmer des Dienstes spielt dies nicht die geringste Rolle. Dies ist jedoch einer der Punkte, bei dem man ein Transportmittel wie SOAP einsetzen kann. Die Art des Dialogs mit dem Dienst ist festgelegt, wobei die Einzelheiten der Implementierung dem Entwickler des Dienstes überlassen bleiben. Beim Adressbuch werden die Methoden, die aufgerufen werden können, während der Registrierung des Dienstes beim Apache SOAP-Router definiert.Listing 5
public Address getAddressFromName(String name)throws IllegalArgumentException{if (name == null){throw new IllegalArgumentException("The name argument must not be null.");}return (Address)name2AddressTable.get(name);}
Listing 7
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"id="urn:AddressFetcher"><isd:provider type="java"scope="Application"methods="getAddressFromName addEntrygetAllListings putListings"><isd:java class="samples.addressbook.AddressBook"static="false"/></isd:provider><isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener><isd:mappings><isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"xmlns:x="urn:xml-soap-address-demo"qname="x:address"javaType="samples.addressbook.Address"java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/><isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"xmlns:x="urn:xml-soap-address-demo"qname="x:phone"javaType="samples.addressbook.PhoneNumber"java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/></isd:mappings></isd:service>
Der Client
Listing 8
package samples.addressbook;import java.io.*;import java.util.*;import java.net.*;import org.w3c.dom.*;import org.apache.soap.util.xml.*;import org.apache.soap.*;import org.apache.soap.encoding.*;import org.apache.soap.encoding.soapenc.*;import org.apache.soap.rpc.*;/*** See \samples\addressbook\readme for info.** @author Matthew J. Duftler (duftler@us.ibm.com)*/public class GetAddress{public static void main(String[] args) throws Exception{if (args.length != 2&& (args.length != 3 || !args[0].startsWith("-"))){System.err.println("Usage:");System.err.println(" java " + GetAddress.class.getName() +" [-encodingStyleURI] SOAP-router-URL nameToLookup");System.exit (1);}// Process the arguments.int offset = 3 - args.length;String encodingStyleURI = args.length == 3? args[0].substring(1): Constants.NS_URI_SOAP_ENC;URL url = new URL(args[1 - offset]);String nameToLookup = args[2 - offset];SOAPMappingRegistry smr = new SOAPMappingRegistry();BeanSerializer beanSer = new BeanSerializer();// Map the types.smr.mapTypes(Constants.NS_URI_SOAP_ENC,new QName("urn:xml-soap-address-demo", "address"),Address.class, beanSer, beanSer);smr.mapTypes(Constants.NS_URI_SOAP_ENC,new QName("urn:xml-soap-address-demo", "phone"),PhoneNumber.class, beanSer, beanSer);// Build the call.Call call = new Call();call.setSOAPMappingRegistry(smr);call.setTargetObjectURI("urn:AddressFetcher");call.setMethodName("getAddressFromName");call.setEncodingStyleURI(encodingStyleURI);Vector params = new Vector();params.addElement(new Parameter("nameToLookup", String.class,nameToLookup, null));call.setParams(params);// Invoke the call.Response resp;try{resp = call.invoke(url, "");}catch (SOAPException e){System.err.println("Caught SOAPException (" +e.getFaultCode() + "): " +e.getMessage());return;}// Check the response.if (!resp.generatedFault()){Parameter ret = resp.getReturnValue();Object value = ret.getValue();System.out.println(value != null ? "\n" + value : "I don't know.");}else{Fault fault = resp.getFault();System.err.println("Generated fault: ");System.out.println (" Fault Code = " + fault.getFaultCode());System.out.println (" Fault String = " + fault.getFaultString());}}}
// Map the types.smr.mapTypes(Constants.NS_URI_SOAP_ENC,new QName("urn:xml-soap-address-demo", "address"),Address.class, beanSer, beanSer);smr.mapTypes(Constants.NS_URI_SOAP_ENC,new QName("urn:xml-soap-address-demo", "phone"),PhoneNumber.class, beanSer, beanSer);
// Build the call.Call call = new Call();call.setSOAPMappingRegistry(smr);call.setTargetObjectURI("urn:AddressFetcher");call.setMethodName("getAddressFromName");call.setEncodingStyleURI(encodingStyleURI);Vector params = new Vector();params.addElement(new Parameter("nameToLookup", String.class,nameToLookup, null));call.setParams(params);
Listing 9
// Invoke the call.Response resp;try{resp = call.invoke(url, "");}catch (SOAPException e){System.err.println("Caught SOAPException (" +e.getFaultCode() + "): " +e.getMessage());return;}// Check the response.if (!resp.generatedFault()){Parameter ret = resp.getReturnValue();Object value = ret.getValue();System.out.println(value != null ? "\n" + value : "I don't know.");}else{Fault fault = resp.getFault();System.err.println("Generated fault: ");System.out.println (" Fault Code = " + fault.getFaultCode());System.out.println (" Fault String = " + fault.getFaultString());}
Webdienste
Wenn Sie nicht gerade in einer Höhle gelebt haben, haben Sie sicher den ganzen Medienrummel um die Web Services mitbekommen. Eine der Technologien, die den Webdiensten zugrunde liegt, ist SOAP: Es wird verwendet, um die Methodenaufrufe auszuführen. Web Services werden von einigen weiteren, SOAP-externen Standards unterstützt, die über das Thema dieses Artikels hinausgehen. Zum Beispiel verwendeten wir ein proprietäres XML-Dokument oder ein Webformular, um unseren Dienst beim Apache-Server zu registrieren. Um Aufrufe bei unserem Dienst zu tätigen, mussten wir vorab wissen, welche Methoden zur Verfügung stehen und wie sie aufgerufen werden können. Web Services definiert Leistungsmerkmale wie z. B. WSDL (Web Services Definition Language), bei dem es sich um ein Verzeichnis handelt, das die Eigenschaften eines Webdienstes definiert und wie er aufgerufen wird. Sicher können Sie das Apache SOAP-System verwenden, um Webdienste anzulegen und mit anderen Webdiensten zu kommunizieren. Es muss jedoch außer SOAP noch weitere Infrastruktur eingerichtet sein.Zusammenfassung
Im zweiten Teil unseres Java-XML-Workshops haben wir Sie in die Open Source API JDOM sowie in die SOAP-Implementierung von Apache eingeführt. JDOM ist für das interaktive Arbeiten mit XML eine großartige Möglichkeit, wenn Sie Java-Entwickler im engeren Sinne sind oder schnell etwas programmieren müssen. Wenn Sie Code in anderen Sprachen schreiben, sind Sie wahrscheinlich gut bedient, wenn Sie die DOM-APIs erlernen und sie überall einsetzen (was Ihre Lernkurve verkürzt).Wenn Sie nur XML-Code in Java schreiben, ist JDOM eine bequeme Alternative zu den notwendigerweise komplexen SAX- und DOM-APIs. Wenn Sie einen XML-Parser besitzen, können Sie direkt mit SOAP arbeiten. Jedoch überlässt man die Details auf der niederen Ebene am besten anderen Entwicklern. Das SOAP Toolkit ist ein elegant konstruiertes System für das Arbeiten mit SOAP, für Server, die entweder in Java oder einer anderen SOAP-fähigen Sprache geschrieben sind.Neal Ford ist Vice President Technology bei der in Atlanta ansässigen DSW Group. Er hat zu den Themen Java und Delphi veröffentlicht und spricht regelmäßig auf internationalen Konferenzen, so auch der JAX2002 oder der Entwickler Konferenz.




