XML Remote Procedure Calls (XML

Werbung
6
XML Remote Procedure Calls (XML-RPC)
Eine bewährte und sehr verbreitete Technik zur Entwicklung von
Client-Server-Anwendungen ist der so genannte Remote Procedure Galt (RPC). Der Client ruft mit einem lokalen Prozeduraufruf einen Dienst auf, der in der Regel auf einem anderen
Rechner angeboten wird. Implementierungen des RPC-Mechanismus unterscheiden sich im Allgemeinen für verschiedene
Programmiersprachen.
In diesem Kapitel wird am Beispiel von XML-RPG gezeigt. wie
das Protokoll HTIP und XML als Kommunikationsformat für den
entfernten Prozeduraufruf eingesetzt werden können. Vom Konzept her ist diese Technik programmiersprachenunabhängig. Wir
werden uns hauptsächlich mit der Java-Implementierung beschäftigen. Die Aufgabe 5 dieses Kapitels nutzt eine PHP-Implementierung auf der Client-Seite.
6.1
Grundkonzept und ein erstes Beispiel
XML-RPG ist ein einfaches Protokoll für den entfernten Prozeduraufruf (RPC). XML-RPC verwendet XML als Datenaustauschformat. Anfrage (Methodenname, Parameter) und Antwort (Rückgabewert des Methodenaufrufs) werden als XML-Dokumente mit
HTTP übermittelt.
Bild 6.1 zeigt den Nutzdatenteil der HTTP-Anfrage (HTIPMethode ist POST) und den Nutzdatenteil der HTIP-Antwort beim
Aufruf der entfernten Methode get Echo.
Bild 6.1'
XML-RPC-Anfrage
< ? xrnl v er e ion= " l .O" e n c o d i n g = " UT F - S " ? ><m e thodC a l l ><m e thodN am e>- echo. g etEcho </m e thodNam e ><p aram e >< pa rem>< v al u e > Hall o < / v alu e></p ar am></p aram ~>-
</m ethodC a l l>-
EchoClient
,.,---,-_--='~~~=--,.,----,--____,-,,---_. EchoServer
< ? xml
v er ~ ion="l.O"
e n c o di n g = " UT F - S " ?>-
<m e thodR e ~p on ~ e ><p aram ~>-
< pa r a m><v a lu e>-H a l lo </ v a lu e></param></p ar am ~>< / met h o d Re~ p o n~e >-
XML-RPC-Antwort
XML-RPC-Anfrage
und Antwott
6 XML Remote Procedure Calls
206
Die Nutzdatenteile werden auch als
-Antwort bezeichnet.
(XML~RPC)
XML~RPC~Anfrage
bzw.
Eine XML-RPC-Anfrage kann mehrere Parameterwerte enthalten
(gemäß der Signatur der entfernten Methode). Eine XML~RPC~
Antwort enthält jedoch genau einen Wert.
Struktur der
XML~RPC~Anfrage
<rrethodCa11>
<methodName>Methodenna~/methodName>
<params>
<param>
<va 1ue>Parameterwert</va 1ue>
</param>
</params>
</methodCa11 >
Struktur der
XML-RPC-Antwort
<methodResponse>
<params>
<pararn>
<val ue>Rückgabewert</val ue>
</param>
</params>
</methodResponse>
Datentypen
Parameter- und Rückgabewerte haben jeweils einen bestimmten
Datentyp. XML~RPC unterstützt insgesamt acht Datentypen
einfache Datentypen (z. B. Zeichenketten. Zahlen) und zusammengesetzte Datentypen (z. B. Arrays).
Die Datentypen werden im nächsten Abschnitt einzeln anhand
von Programmbeispielen vorgestellt.
Spezifikation
Die Spezifikation von XML~RPC wurde 1999 von Dave Winer
veröffentlicht. Sie umfasst nur wenige Seiten und kann unter der
Adresse
http://www.xm1rpc.com
nachgeschlagen werden.
Implementierungen von XML-RPC existieren für verschiedene
Programmier- und Skriptsprachen (z. B. Java. Perl. PHP. Python),
Somit kann beispielsweise ein PHP-Client mit einem Java-Server
kommunizieren (siehe Aufgabe 5).
Apache XML~RPC
Wir nutzen im Folgenden die Java~Implementierung Apache
XML~RPC der Apache Software Foundation in der Version 3.1.3.
6.1
Grundkonzept und ein erstes Beispiel
207
Die erforderlichen Bibliotheken GAR-Dateien) können von der
Website
http://ws.apache.org/xmlrpc/
heruntergeladen werden.
Die JAR-Dateien können in ein Verzeichnis Ihrer Wahl kopiert
werden.
Die Version 3.1.3 von Apache XML-RPC erfüllt die offizielle XMLRPC-Spezifikation. bietet aber auch Erweiterungen (Unterstützung zusätzlicher Datentypen, Performancesteigerung durch
Nutzung des so genannten Streaming-Verfahrens, Datenkompression), die in einer reinen Java-Umgebung sinnvoll eingesetzt
werden können. Wir beschränken uns hier hauptsächlich auf die
Vorstellung der zur XML-RPC-Spezifikation kompatiblen Aspekte.
Lediglich Programm 6.10 geht in einem Aspekt auf die Erweiterungen ein.
Eine Weiterentwicklung von XML-RPC ist SOAP. das vom World
Wide Web Consortium (W3C) definiert wird und neben anderen
Standardtechnologien zur Entwicklung von so genannten Web
Services genutzt wird (siehe Kapitel 9). SOAP war ehemals Abkürzung für "Simple übject Access Protocol" und ist heute ein
eigenständiger Name.
SOAP
Im Vergleich zu SOAP hat XML-RPC einen geringeren Leistungsumfang, ist aber auch wesentlich einfacher in der Handhabung.
Für viele Anwendungen erweist sich XML-RPC als völlig ausreichend und kann auch für die Implementierung von Web Services eingesetzt werden.
Zur Implementierung eines XML-RPC-Servers stellt Apache XML- WebServer und
RPC die Klassen
XmlRpcServer
arg. apache. xm1rpc webserver . WebServer und
org.apache.xmlrpc server. XmlRpcServer
zur Verfügung.
WebSer ver implementiert einen speziell für die Behandlung von
XML-RPC-Anfragen geeigneten HTTP-Server. der in eigene Applikationen eingebettet werden kann. Dieser ist zu Testzwecken
sehr gut geeignet. Für höhere Ansprüche in Bezug auf Performance und Stabilität sind ausgereifte Servlet-Container wie beispielsweise Apache Tomcat besser geeignet.
Die Klasse Xm1RpcS er ver verarbeitet die XML-RPC-Anfragen.
WebServer( int port)
erzeugt einen Server mit der Portnummer port.
6 XML Remote Procedure Calls
208
(XML~RPC)
WebServer~Methoden,
vOl d start() throws java.lo. IOException
startet den Server.
voi d shutdown()
stoppt den Server.
Die Methode getXm1RpcServer () liefert ein Xm1RpcServer~Objekt.
Handler
Ein so genannter Hand/er ist eine entfernt aufrufbare Methode.
Folgende Bedingungen müssen erfüllt sein,
•
Die Methode muss pub1i c sein, nicht stati c sein und darf
nicht den Rückgabetyp voi d haben.
•
In der Klasse, die den Handler implementiert, muss der parameterlose Standardkonstruktor implizit oder explizit vorhanden sein.
Die
Klasse
arg. apache. xm1rpc ser ver. PropertyHandl erMappi ng
kann mehrere Handler verwalten.
Ihre Methode
void addHandler(String key. Class typ ) t hrows XmlRpc Exception
fügt die Handler-Klasse typ mit dem Namen key hinzu. Hierzu
wird das Class-Objekt der Handler-Klasse angegeben,
Klassenname. cl ass.
Eine Ausnahme vom Typ arg. apache. xm 1rpc. Xm 1Rpc Excepti on
wird generell ausgelöst, wenn der Server einen Fehler meldet.
voi d removeHandler(String key)
entfernt alle Handler mit dem Namen key.
Mit dem Aufruf der Xm1RpcServer~Methode set Handl erMappi ng
werden die Handler für das Xm1RpcServer~Objekt registriert.
setHandlerMapping(propertyHandlerMapping)
Programm 6.1
Das folgende Beispiel demonstriert eine einfache
Anwendung.
XML~RPC~
Die Klasse Echo implementiert die Methoden get Echo und
get Echo Wi thDate, die ein "Echo" ohne bzw. mit Serverdatum zurückgeben.
6.1 Grundkonzept und ein erstes Beispiel
lmport java text SlmpleDateFormat;
lmport java.utll.Date;
209
Echo
public class Echo (
public String getEcho(String s) (
return 5;
public String getEchoWithDate(String s) (
SimpleDateFormat f ~ new SimpleDateFormat(
"dd.l'M.yyyy HH:mm:ss"):
return "[" + f.format(new Date()) + "] " + s:
Die Klasse EchoServer erzeugt eine Instanz der Klasse WebServer,
registriert den Echo-Dienst mit dem Namen "echo" und startet
dann den Server.
lmport arg apache xmlrpc server.PropertyHandlerMapplng;
lmport arg apache xmlrpc server.XmlRpcServer;
lmport arg apache xmlrpc webserver.WebServer;
EchoServer
public class EchoServer (
public static void main(String[] args) throws Exception (
int port ~ Integer.parselnt(args[O]):
PropertyHandlerMapping phm ~ new PropertyHandlerMapping():
phm. addHandler("echo". Echo.cl ass ) :
WebServer webServer = new WebServer(port);
XmlRpcServer server = webServer.getXmlRpcServer();
server.setHandlerMapping(phm):
webServer.start():
Zur Implementierung eines XML-RPC-Client stellt Apache XML- XmlRpcClient
RPC die Klasse
org.apache xmlrpc.client. XmlRpcClient
zur Verfügung.
Mit Hilfe der Klasse
org.apache.xmlrpc client.XmlRpcClientConfiglmpl
kann ein Xm 1RpcCl i ent-Objekt konfiguriert werden.
6
210
XML Remote Procedure Calls (XML~RPC)
Die Xm1RpcCl i ent Conf i 9Imp1-Methode
voi d setServerURL(java.net.URL url)
legt den URL des Servers fest. Im Beispiel, http ://l oca 1host:
50000.
Mit dem Aufruf der Xm1RpcCl i ent-Methode setConfi 9 wird die
Konfiguration confi 9 für den Client gesetzt. setConfi g(confi q ).
execute
Die Xm1RpcCl i ent-Methode
Object execute(Str ing method. Object[] params)
throws XmlRpc Exception
erzeugt eine XML~RPC~Anfrage und sendet sie mittels HTIP zum
Server. Die zurückgeschickte XML~RPC~Antwortwird geparst und
als Objekt vom Typ Object zurückgegeben.
Der String rret hod hat den Aufbau:
Dienstname.Methodenname
Diens t name ist der Name, unter dem der Dienst auf der Serverseite registriert ist. t1et hodenname ist der Name der Methode, die der
Dienst implementiert hat. Das Array params enthält die erforderliehen Parameter.
Wie die Parameter passend zur Signatur der Methode erzeugt
werden müssen, zeigen die nächsten Programmbeispiele.
EchoCl i ent ruft beide Methoden des Echo-Dienstes mit dem Argument "Hallo" auf.
EchoClient
import ja va.net.URL:
import org.apache xmlrpc client XmlRpcClient:
import org.apache xmlrpc client XmlRpcCli entConfig Impl :
public class Ec hoCl i ent (
public static voi d main(String args[]) throws Exception (
URL url ~ new URL(args[O]):
Xml RpcCli ent Conf i gl mpl config ~
new Xm1RpcCl i ent Conf i 9Imp1 ( ) :
confi g. setServerURl(ur1 ):
XmlRpcClient cli ent ~ new XmlRpcCli ent():
cli ent.setConfig(config):
Object[] params ~ {"Hallo"}:
String s ~ (String) client.execute(
"echo. get Echo". params) :
System.out.println (s) :
6.1
Grundkonzept und ein erstes Beispiel
211
String t ~ (String) client execute(
"echo. getEchoWi thDate". params);
System.out.println(t);
Die einzelnen Schritte beim entfernten Methodenaufruf sind:
Ablauf beim
1. Das Client-Programm erzeugt eine Xm l RpcCll ent-Instanz, kon- entfernten
figuriert sie und ruft dann die Methode execute mit Angabe Metbodenaufruf
des Dienstnamens, des Methodennamens und der Parameter
auf.
2. Diese Angaben werden in ein XML-Dokument verpackt und
per HTTP-POST an den Server geschickt.
3. Der Server empfängt die HTIP-Anfrage und leitet die Verarbeitung des XML-Dokuments ein.
4. Das XML-Dokument wird geparst und anschließend die angegebene Methode des Dienstes aufgerufen.
5. Die Methode übergibt das Ergebnis an
den XML-RPCVerarbeitungsprozess, der dann das Ergebnis in ein XMLDokument verpackt.
6. Der Server schickt das XML-Dokument als Antwort auf die
HTIP-Anfrage zurück.
7. Der XML-RPC-Client parst das Xlvll-Dokument, extrahiert den
Rückgabewert und übergibt diesen als Objekt an das ClientProgramm.
Dienste
_~f-------------1~
HTTP
Client
Transformation
Server
Transfonnation
Bevor das Werkzeug Ant mit build.xml aus der Programmsammlung genutzt werden kann, muss die Umgebungsvariable
XMLRPC_PATH gesetzt werden. Hier müssen alle erforderlichen JARDateien aufgeführt werden:
set XMLRPC _PATH~Verzei chni si xxx. ja r: .
1. Compilieren:
ant compll e
2. Server starten:
ant server
3. Client starten:
ant cl i ent
Ausgabe:
Ha 11 0
[20.02.2010 16;34;34J Hallo
Bild 62.'
Kommunikation
zwischen Client
und Server
6 XML Remote Procedure Calls
212
(XML~RPC)
Alternativ können die obigen drei Schritte im Eingabeaufforderungsfenster wie folgt ausgeführt werden,
mkdir bin
javac -cp %XMLRPC_PATH% -d bin src/*.java
start java -cp bin;%XMLRPC_PATH% EchoServer 50000
java -cp bin;%XMLRPC_PATH% EchoClient http;//10calhost;50000
6.2
XML-RPC-Datentypen
Zu den einfachen Datentypen gehören:
•
Ganzzahl,
•
Gleitkommazahl,
•
Wahrheitswert,
•
Zeichenkette,
•
Datum/Zeit,
•
Binärdaten.
Zusammengesetzte Datentypen sind:
•
Array und
•
Struktur.
Ein Array-Element kann einen Wert vom einfachen oder zusammengesetzten Datentyp enthalten.
Eine Struktur ist eine Folge von Elementen, die jeweils aus einem
Namen und einem Wert bestehen. Der Name muss eine Zeichenkette sein, der Wert kann vom einfachen oder zusammengesetzten Datentyp sein.
Die folgende Tabelle gibt eine Übersicht.
Datentypen
XML-Tag-Name
i4
Java-Typ für execute
java 1ang. Integer
Java-Typ für Handler
lnt
doub 1e
java 1ang. wub1e
doub 1e
boolean
java 1ang. Boo 1ean
boolean
str i ng
java 1ang String
ja va 1ang String
dateTi lTl2 i s08601
java uti 1. Date
java uti 1. Date
base64
byte[]
byte[]
array
java 1ang Object[]
java 1ang Object[]
struct
java uti 1.Map
java util .Map
6.2
XML-RPC-Datentypen
213
Die erste Tabellenspalte enthält die Namen der Tags für das vom
XML-RPC-Protokoll verwendete XML-Dokument. Diese Tags markieren den Datenwert zum entsprechenden Datentyp. Die zweite
Spalte enthält die Entsprechungen in Java für den Aufruf der
XmlRpcCl i ent-Methode execute. Die Datentypen der dritten Spalte
werden als Parameter- bzw. Rückgabetypen der Handler benutzt.
void-Metho den sind als Handler-Methoden nicht erlaubt. Der
Wert null ist weder als Argumentwert noch als Rückgabewert
erlaubt.
Mit Programm 6.2 kann die Handhabung der verschiedenen Programm 62
Datentypen getestet werden.
lmport arg apache xmlrpc server.PropertyHandlerMapping;
import arg apache xmlrpc server.XmlRpcServer;
import arg apache xmlrpc webserver.WebServer;
DatentypTestServer
public class Datenty pTestSer ver (
public static voi d main(String[] args) throws Except i on (
in t port ~ Int eger .par selnt( ar gs [O] ) ;
PropertyHandl erMapping phm ~ new PropertyHandlerMapping();
phm. addHandl er ("test". DatentypTest . cl ass) ;
WebServer webServer
Xml RpcServer server
=
=
new WebServer(port);
webServer .getXmlRpcServer();
server .s et HandlerMapping (phm);
webServer.s tart();
Die Handler-Klasse DatentypTest enthält für jeden XML-RPC- DatentypTest
Datentyp eine Testmethode.
import
import
import
import
import
import
java. i o ByteArrayOutputStream;
java. io Fi l el nput St ream;
java. io. IO Excepti cn:
java. io. InputStream;
java. util.Date;
java. util.Map;
public class DatentypTest
/ / Ganzzahl
public int test lnt(int x)
return x:
6 XML Remote Procedure Calls (XML~RPC)
214
11 Gleitkommazahl
public double testDouble(double x)
re turn x;
11 Wahr hei t swert
public boolean testßoolean(boolean x)
return x;
11 Zei chenket t e
publ i c String testStr ing(String x)
return x;
11 Datum/Zei t
public Date t est Dat eTime(Dat e x)
return x;
11 Bi närdaten
public byte[] testBase64() throws IOExcept i on (
Input St ream in ~ new File lnputStream("java.gif");
ByteArrayOutputStream out ~ new ByteArrayOutputStream();
l nt c :
while ((c ~ in.read()) !~ -1) (
out.write(c);
}
in.close();
return out. toByteArray();
11 Array
publ i c Object[] testArray(Object[] x) (
return x;
11 Struktur
public Map<String, String> tes tStruct(
Map<String, String> x) {
return x;
DatentypTestClient
Die Klasse DatentypTestCl ient enthält für jeden
Datentyp den Aufruf der zugehörigen Testmethode.
XML~RPC~
6.2
XML-RPC-Datentypen
lmport java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.URL;
import java.utll.Date;
import java.util.HashMap;
import java.utll.Map;
import arg apache.xmlrpc.XmlRpcException;
import org apache.xmlrpc.client.XmlRpcClient;
import org apache.xmlrpc.client.XmlRpcClientConfiglmpl;
public class DatentypTestClient (
@SuppressWarnings("unchecked")
public static void main(String args[J) throws Exception (
URL url ~ new URL(args[OJ);
String typ ~ args[lJ;
XmlRpcClientConfiglmpl config ~
new XmlRpcClientConfiglmpl();
config.setServerURL(url);
XmlRpcClient client ~ new Xml RpcCl i ent ( ) ;
client.setConfig(config);
/ / Ganzzahl
if (typ.equals("int")) (
System.out.println(typ);
Object[J params ~ { 1234 };
int r = (Integer) client.execute(
"test.testlnt". params);
System.out.println(r);
System.out.println();
}
// Gleitko mmazahl
else if (typ.equals("double"))
System.out.println(typ);
Object[J params ~ { 123.456 };
double r ~ (Doubl e ) client.execute("test.testDouble".
params);
System.out.println(r);
System.out.println();
}
// Wahrheitswert
else if (typ.equals("boolean"))
System.out.println(typ);
Object[J params ~ { true };
boolean r
=
(Boolean) cl ient.execute("test.testBoolean" ,
params);
System.out.println(r);
System.out.println();
215
6 XML Remote Procedure Calls
216
(XML~RPC)
// Zeichenkette
else if (typ.equals("string"))
System.out.println(typ);
Object[J pararns ~ ("<- A & 0 -->" );
String r ~ (String) client.execute("test.testString".
params) ;
System.out.println(r);
System.out.println();
)
//Oatum/Zeit
else if (typ.equals("dateTirne"))
System.out.println(typ);
Object[] params ~ ( new Date() );
Date r ~ (Date) client.execute("test.testDateTi rne".
params) ;
System.out.println(r);
System.out.println();
)
/I Bi närdaten
else i f (typ. equa ls ("base64"))
System.out.println(typ);
Object[] params ~ ();
byte[] r ~ (byte[]) client.execute("test.testBase64".
params) ;
OutputStream out ~ new Fi leOutputStream("test.gi f");
for (int i ~ 0; i < r.length; i++) (
out.write(r[i]);
)
out. fl ush () ;
out.close() ;
System.out.println();
)
// Array
else if (typ.equals("array"))
System.out.println(typ);
Object[] array ~ ( "Das ist ein String". 4711 );
Object[] params ~ ( array );
Object[] r ~ (Object[]) client.execute("test.testArray".
params) ;
for (Object obj
r) (
System.out.println(obj);
)
System.out.println();
)
/ / Struktur
else i f (typ. equa ls ("struct"))
System.out.println(typ);
Map<String. String> map ~ new HashMap<String. String>();
map.put("Vorname". "Hugo");
map.put("Nachnarne". "Meier");
Object[] pararns ~ ( map );
6.2
XML-RPC-Datentypen
Map<String. String> r ~ (Map<St r i ng . String»
.execute ("test. testStruct", params);
System.out.println(r.get("Vorname") + " "
+ r. get( "Nachname"));
System.out.println();
217
client
)
11 Ausnahme
else if (typ.equals("fault"))
try (
System.out.println(typ);
Objecte] params ~ ();
c II ent. execute ("test. testF aul t". params);
catch (XmlRpcException e) (
System.out.println(e.getMessage());
Für jeden XML-RPC-Datentyp werden nun im Folgenden
•
der Parameterwert im XML-Dokument der XML-RPC-Anfrage
C<value>-Tag).
•
der Rückgabewert im XML-Dokument der XML-RPC-Antwort
C<value>-Tag) und
•
die Ausgabe des Client
aufgeführt.
XML-RPC-Anfrage;
Ganzzahl
<i4>1234</i4>
XML-RPC-Antwort
<i4>1234</i4>
Ausgabe des Client-Programms:
1234
XML-RPC-Anfrage;
<double>123.456</double>
XML-RPC-Antwort
<double>123.456</double>
Ausgabe des Client-Programms:
123.456
Gleitkommazahl
6 XML Remote Procedure Calls
218
wabraeosioen
(XML~RPC)
XML~RPC~Anfrage,
<boolean>l</boolean>
XML~RPC~Antwort
<boolean>l</boolean>
Ausgabe des Client-Programms:
true
Zeichenkette
XML~RPC~Anfrage,
<-
A & 0 -->
XML~RPC~Antwort,
<-
A & 0 -->
Ausgabe des Client-Programms:
<-
A & 0 -->
Wenn wie hier kein Datentyp angegeben ist, wird <st r i ng> unterstellt. Die Zeichen <; > und & haben in XML eine Sonderrolle
und werden daher umcodiert.
Datum/Zeit
XML~RPC~Anfrage,
<dateTi me.iso8601>20100220TI7 11;36</dateTi me.iso8601>
XML~RPC~Antwort,
<dateTi me .iso8601>20100220TI7 11 36</dateTi me.iso8601>
Ausgabe des Client-Programms:
Sat Feb 20 17;11;36 CET 2010
Binärdaten
Die
XML~RPC~Anfrage enthält
kein <va1ue--Tag.
XML~RPC~Antwort,
<base64>ROl GOOl hNABYAPcAAP ... </base64>
nutzt das Codierungsverfahren Base64, um Binärdaten
technisch gesichert in Xlvll-Srrukturen zu übertragen. 24 Bit lange Gruppen der Binärdaten werden in vier Bitfolgen von jeweils
6 Bit zerlegt. Diese 6 Bit werden mit Hilfe der US~ASCll~Zeichen
A ~ Z, a ~ z, 0 ~ 9, -t-, / und ~ codiert (http;//www.ietf.org/rfc/
rfc2045. txt).
XML~RPC
Ausgabe des Client-Programms:
Der Client speichert die übertragenen Daten in der Datei test.gif
6.2
XML-RPC-Datentypen
XML-RPC-Anfrage,
219
Array
<array>
<data>
<val ue>Das lst eln Str ing</ value>
<val ue><i 4>4711</i 4></value>
</data>
</array>
XML-RPC-Antwort
<array>
<data>
<value>Das ist ein String</ value>
<value><int>4711</int></ value>
</data>
</array>
Ausgabe des Client-Programms:
Das ist ein String
4711
XML-RPC-Anfrage,
Struktur
<struct>
<member>
<name>Nachname</name>
<value>Meier</ value>
</ member>
<member>
<name>Vorname</name>
<val ue>Hugo</val ue>
</ memb er>
</struct>
XML-RPC-Antwort
<struct>
<member>
<name>Nachname</name>
<value>Meier</value>
</ member>
<member>
<name>Vorname</name>
<val ue>Hugo</ val ue>
</member>
</struct>
Ausgabe des Client-Programms:
Hugo Mei er
Es wird eine Ausnahme vom Typ Xm1RpcExcept i on ausgelöst, da Ausnahme
die Methode testFault nicht existiert.
6 XML Remote Procedure Calls (XML~RPC)
220
Die komplette
XML~RPC~Antwort
<?xml verslon="1.0" encodlng="U TF 8"?>
<rrethodRespose>
<fault><value><struct>
<rrember>
<name>faultCode</name>
<value><l4>O</l4></value>
</member>
<rrember>
<name>faultStrlng</name>
<value>No such handler: test testFault</value>
</member>
</struct></value></fault>
</methodResponse>
Ausgabe,
No such handler: test.testFault
Das Programm MyReporter (Kapitel 5.6) kann genutzt werden, um
die zwischen Client und Server ausgetauschten Daten aufzuzeichnen.
MyReporter starten (Kommando in einer Zeile):
start java -cp .. /Prog0506/bin MyReporter 40000 localhost
50000 logRequest.txt logResponse.txt
logRequest.txt enthält die H'I'Tl'<Anlragen und logResponse.txt die
HTIP~Antworten.
Server starten:
start java -cp bin %XMLRPC PATH% OatentypTestServer 50000
Client starten (Kommando in einer Zeile):
java -cp bin:%XMLRPC_PATH% OatentypTestClient
http://localhost:40000 int
6.3
6.3
Komplexe Datenstrukturen
221
Komplexe Datenstrukturen
Die Beispiele dieses Kapitels zeigen, wie komplexe Datenstrukturen mit Hilfe einfacher und zusammengesetzter XML-RPCTypen in ein für XML-RPC geeignetes Format transformiert werden können.
Die Handler-Klasse War enkorb verwaltet eine Reihe von Bestell- Programm 63
positionen mit den Attributen id, name, preis und menge in einem Vector-Objekt und bietet die folgenden Methoden an,
int addPosltlon(lnt rd . String name, double pr eis, i nt menge)
fügt eine Position ein.
Obj ect[] getPositionen()
liefert alle Positionen des Warenkorbs. Jedes Arrayelement des
Rückgabewertes ist selbst wieder ein Arra y, das als Elemente die
Attribute einer Position enthält. Bild 6.3 zeigt die zugehörige
XML-Struktur.
<a r r ay>
<data>
<va lue>
Bild 63·
Warenkorb als
Array von Arrays
<a r r-ay>
<da ta>
<val ue><i4>lOOO</ i4></val ue>
<value>Hammer</va lue>
<value><doub le>2.S</ double></va lue>
<value><i4>lO</ i4></va lue>
</data>
<y ar r ay>
</va l ue>
<va lue>
<a r r -ay >
<data>
<value><i4>lOlO</ i4></value>
<value>Zange</va lue>
<value><doub le>3.99</ doub le></va lue>
<value><i4>8</ i4></va lu e>
</da ta>
</ar: r:ay>
</va lue>
</ data>
</ar: r:ay>
publlc class Pos l t l on
prlvate int td:
prlvate Strlng name;
prl vate double prels;
prl vate lnt me nge ;
Position
6 XML Remote Procedure Calls
222
(XML~RPC)
publlc Posltlon(lnt ld, Strlng name, double prels,
i nt rre nge ) (
th is.id ~ id;
th is name = name;
this preis = preis;
this me nge = me nge ;
public Object[] getPosition() {
Object[] array ~ new Object[ 4] ;
array[O] ~ id;
array[!] ~ narre;
array[2] ~ preis;
array[3] ~ rre nge ;
r eturn array;
Warenkorb
import ja va.util .Vector;
public class Warenkor b (
private static Vector<Posit ion> korb
Vector<Position>();
=
new
publ i c boolean addPos ition(int id. String name.
double pre is. int rre nge ) (
korb.add(n ew Position(id. name. preis. rre nge) ) ;
r eturn true;
public Object[] get Posi t i onen( ) (
Vector<Object> v = new Vector<Object> ();
f or (Position pos korb) (
v.add (pos.getPosition());
}
return v. toArray();
Server
import arg. apache xml rpc server. PropertyHandl erMapping;
import arg. apache xmlrpc server.XmlRpcServer;
import arg. apache xmlrpc webserver.WebServer;
public class Server (
public static voi d main(String[] args) throws Except i on (
int port ~ Int eger .parsel nt (args [ O] ) ;
PropertyHandlerMapping phm ~ new Property HandlerMapping();
phm. add Handl er( "waren korb". Warenkorb.cl ass);
6.3
Komplexe Datenstrukturen
223
W
ebSer ver webServer = new W
ebSer ver (port ) ;
XmlRpcServer server = webServer.getXmlRpcServer();
server.set HandlerMapping (phm);
webServer.start();
import java.net.URL;
Client
import org apache xmlrpc client.XmlRpcCli ent;
import org apache xmlrpc client.XmlRpcCli entConfig Impl;
public class Client (
public static void main(String args[]) throws Except i on (
URL url ~ new URL(args[O]);
Xml RpcCl ientConfig Impl config ~
new XmlRpcClientConfig Impl();
config.setServerURL(url);
XmlRpcClient client ~ new XmlRpcClient();
client.setConfig(config);
Object[] params l ~ ( 1000. "Hammer". 2.5. 10 l:
cl i ent .execute ("wa renkorb . addPos i ti on". pararns 1) ;
Object[] params2 ~ ( 1010. "Zange". 3.99. 8 I:
cl i ent .execute ("wa renkorb . addPos i ti on". params2) ;
Object[] params3 ~ ();
Object[] result ~ (Object[] ) client.execute(
"waren korb .getPosi ti onen". params3);
for (Object obj resul t ) (
Object[] array ~ (Object[]) obj;
+ (Integer) array[O]);
System.out.println(" Id;
System.out.println ("Narre; " + (String) array[l]);
System.out.println ("Preis;
+ (DJuble) array[2]);
System.out.println(" l'enge; " + ( Integer) array[3]);
System.out.println();
Das Beispiel zeigt auch, dass die Kenntnis der Signatur (mit Achtung
Rückgabetyp) eines Handlers in der Regel alleine nicht ausreicht,
um das Ergebnis des Methodenaufrufs weiterzuverarbeiten. Zusätzlich sind die Datenstruktur (Array von Arrays) und ihre Semantik zu erläutern.
6 XML Remote Procedure Calls
224
Programm 6.4
(XML~RPC)
Der folgende Dienst ermöglicht die Suche nach Personen, die
auf der Serverseite in einer Datenbank gespeichert sind. Das
Suchergebnis ist eine Liste von Personen mit den zugehörigen
Adressen.
Personen und Adressen sind in einer MySQL-Datenbank in zwei
miteinander verknüpften Tabellen gespeichert.
create table person (
persld lnteger not null auto_lncrement,
name varchar(30),
vorname varchar(30),
gebdatum date.
primary key (persid)
create table adresse (
adressld lnteger no t null auto_lncrement,
persl d l nteger not null,
plz char(5).
ort varchar(30).
primary key (adressid).
forelgn key (persld) references person (persid)
Die Klasse PersonQuery enthält die Methode
ArrayList<Person> getPersonen(String name),
die mit Hilfe des Suchbegriffs narre, der auch so genannte Wild~
card-Zeichen wie % und enthalten darf, in der Datenbank nach
Personen, deren Namen dem Kriterium entsprechen, sucht und
eine Liste von Person-Objekten zurückliefert. Ein Person-Objekt
enthält eine Liste von möglicherweise mehreren AdresseObjekten.
Bild 6.4.·
Die Person-Adresse-
Beziehung
Person
name
vorname
gebdatum
adressen
1
0..*
Adresse
piz
ort
6.3 Komplexe Datenstrukturen
lmport java text.SlmpleDateFormat;
lmport java.utll.ArrayLlst;
lmport java.utll.Date;
public class Person (
prlvate Strlng name;
prlvate Strlng vorname;
private Date gebdatum;
prlvate ArrayLlst<Adresse> adressen;
public void setName(String name) (
thls.name = name;
public String getName() (
return name;
public void setVorname(String vorname) (
thls.vorname = vorname;
public String getVorname()
return vorname;
public void setGebdatum(Date gebdatum) (
this.gebdatum ~ gebdatum;
public Date getGebdatum()
return gebdatum;
publlC vOld setAdressen(ArrayLlst<Adresse> adressen) {
thls.adressen = adressen;
public ArrayList<Adresse> getAdressen()
return adressen;
public void print() {
System. out. pri nt 1n(" Name;
+ name);
System.out. pr i nt ln ("Vorname: " + vorname);
SimpleDateFormat f ~ new SimpleDateFormat("dd l+1 .yyyy" ) ;
if (gebdatum !~ null)
System. out. pri nt 1n( "Gebdatum; " + f. format (gebdatum));
else
System.out println("Gebdatum; null ");
225
Person
6 XML Remote Procedure Calls
226
if (adressen !~ null) {
für (Adresse adr adressen)
adr.print();
Adresse
publlC class Adresse
private String plz;
prlvate Strlng ort;
public void setPlz(String plz) (
this plz ~ plz;
public String getPlz() (
return pl z:
public voi d setOrt(String ort) (
this ort = ort;
public String getOrt() (
return ort;
public void print() (
System.out.println("PLZ;
System. out. pri nt 1n( "Ort;
PersonQuery
import
import
import
import
java
java
java
java
+ pl z);
+ ort);
io.File InputStream;
io.IOException;
sql .Connection;
sq l . Dri verManager ;
import java sql.PreparedStaterrent;
import java sql.ResultSet;
import java sql .SOLException;
import java uti l.ArrayL ist;
impor t java util.Properties;
public class PersonQuery
pri vate Stri ng ur l ;
private String user;
private String password;
(XML~RPC)
6.3 Komplexe Datenstrukturen
public PersonQuery() {
try (
FilelnputStream input
~
new FilelnputStream(
"dbconnect. propertles");
Propertles prop = new Propertles();
prop.load (i nput) ;
input.close();
url ~ prop.getProperty("url");
user ~ prop. getProperty( "user");
password ~ prop. getProperty( "password");
catch (IOException e) (
System.err.println(e);
publlC ArrayLlst<Person> getPersonen(Strlng name) {
ArrayLlst<Person> personen = new ArrayLlst<Person>();
Connectlon con = null;
try (
con = DrlverManager.getConnectlon(url, user, password);
Strlng sql = "select persld, name, vorname, gebdatum "
+
+
"fram person where name II ke ? "
"order by name, vorname";
PreparedStatement pstmt ~ con.prepareStatement(sql);
pstmt.setString(I. name);
ResultSet rs ~ pstmt.executeQuery();
while (rs.next()) (
Person p = new Person();
int persid ~ rs.getlnt(l);
p.setName(rs.getString(2));
p.setVorna me(rs.getString(3));
p.setGebdatum(rs.getDate(4));
p.setAdressen(getAdressen(persid. con));
personen.add(p);
}
rs .close();
pstmt.close();
catch (SQLException e)
System.err.println(e);
finally {
try {
if (con !~ null)
con.close();
catch (SQLException e) {
}
return personen;
227
6 XML Remote Procedure Calls (XML~RPC)
228
prlvate ArrayLlst<Adresse> getAdressen(lnt persld,
Connection con) {
ArrayList<Adresse> adressen = new ArrayList<Adresse>();
try (
String sql = "select pl z , ort fram adresse"
+ "where persid
=
? order by pl z":
PreparedStatement pstmt ~ con prepareStatement(sql);
pstmt.setlnt(l. persid);
ResultSet rs ~ pstmt.executeQuery();
whi 1e (rs. next ()) (
Adresse a = new Adresse();
a.setPlz(rs.getString(l));
a.setürt(rs.getString(2));
adressen.add(a);
}
rs.close();
pstmt. cl ose () ;
catch (Exception e) (
System.err.println(e);
}
return adressen;
11 Testprogramm
public static void main(String[] args) throws Exception
PersonQuery query = new PersonQuery();
ArrayList<Person> personen
for (Person pers
=
query.getPersonen("%");
personen) {
pers.print();
System.out.println();
Die DB-Verbindungsparameter befinden sich in der Datei
dbconnect.properties. Die Methode getPersonen kann unabhängig
von XML~RPC getestet werden (main).
Um das Suchergebnis mittels XML~RPC zum Client zu übertragen,
muss das Modell aus Bild 6.4 in eine für XML~RPC geeignete
Struktur transformiert werden. Diese darf nur die Datentypen aus
Kapitel 6.2 enthalten.
Diesem Zweck dienen die beiden Hilfsklassen PersonHelper und
AdresseHel per. Sie enthalten jeweils zwei statische Methoden:
eine, die ein Adresse- bzw. Person-Objekt in eine Map transformiert, und eine, die aus einer Map wiederum ein Adresse- bzw.
Person-Objekt erzeugt. Erstere wird auf der Serverseite, letztere
auf der Clientseite benötigt.
6.3 Komplexe Datenstrukturen
229
import java.util.HashMap;
lmport java.utll.Map;
AdresseHelper
public class AdresseHelper (
private final static String PLZ
private final static String ORT
~
"plz":
~
"ort.":
public static Map<String. Object> toMap(Adresse adr) (
Map<String. Object> map ~ new HashMap<String. Object>();
String plz ~ adr.getPlz();
if (plz !~ null)
map.put(PLZ. plz);
String ort ~ adr.getOrt();
if (ort !~ null)
map.put(ORT. ort);
re turn map;
public static Adresse fromMap(Map<String. Object> map) (
Adresse adr = new Adresse();
Object obj ~ map.get(PLZ);
if (obj !~ null)
adr.setPlz((String) obj);
obj ~ map.get(ORT);
if (obj !~ null)
adr.setOrt((String) obj);
return adr;
lmport
lmport
lmport
lmport
java. uti 1.ArrayList;
java. uti 1.Date;
java. uti 1.HashMap;
java. uti 1.Map;
public class PersonHelper (
private final static String
pri vate final static String
pri vate final static String
private final static String
PersonHelper
NAME ~ "narre";
VORNAME ~ "vornarre";
GEBDATUM ~ "gebdatum";
ADRESSEN ~ "adressen";
public static Map<String. Object> toMap(Person pers) (
Map<String. Object> map ~ new HashMap<String. Object>();
6 XML Remote Procedure Calls
230
(XML~RPC)
String name ~ pers getName ();
if (name !~ null)
map.put(NAME. name);
Strlng vorname = pers.getVorname();
i f (vorname ! ~ null)
map.put(VORNAME. vorname);
Date gebdatum ~ pers.getGebdatum();
if (gebdatum !~ null )
map.put(GEBDATUM. gebdatum);
ArrayLlst<Adresse> adressen = pers getAdressen();
if (adressen !~ null) (
Object[] array ~ new Object[adressen.size()];
i nt i ~ D;
for (Adresse adr adressen) (
array[i++] ~ AdresseHelper.toMap(adr);
}
map .put (ADRESSEN. array);
return map;
@Suppress Warnings("unchecked" )
public static Person fromMap(Map<String. Object> map) (
Person pers = new Person();
Object obj ~ map.get(NAME);
if (obj !~ null)
pers.setName((String) obj);
obj ~ map.get(VORNAME);
if (obj !~ null)
pers.setVorname((String) obj);
obj ~ map.get(GEBDATUM);
i f (obj !~ null)
pers.setGebdatum((Date) obj) ;
obj ~ map.get(ADRESSEN);
i f (obj !~ null) (
ArrayLlst<Adresse> adressen = new ArrayLlst<Adresse>();
(Object[]) obj) {
for (Object 0
adressen. add(AdresseHelper. fromMap(
(Map<String. Object» 0));
pers setAdressen(adressen);
6.3 Komplexe Datenstrukturen
231
return pers;
Der Handler getPersonen der Klasse PersonQueryHandler nutzt die
PersonQuery-Methode getPersonen und transformiert die PersonObjekte. Rückgabewert ist ein Array von Maps. Eine jede Map
entspricht einer gefundenen Person. Bild 6.5 zeigt das Ergebnis
einer Transformation.
lmport java.utll.ArrayLlst;
public class PersonQueryHandler (
prlvate statlc PersonQuery query
PersonQueryHandler
=
new PersonQuery();
public Object[] getPersonen(String name) (
ArrayLlst<Person> personen = query.getPersonen(name);
Object[] array ~ new Object[personen.size()];
if (personen !~ null) {
int l
=
0;
for (Person pers personen) {
array[i++] ~ PersonHelper.toMap(pers);
}
return array;
Bild 6.5'
Eine Person
Abbildung der
Person-AdresseBeziehung auf
Map und Array
Map
name
...
vorname
...
Object [l
gebdatum ...
adressen
I
Map
piz
...
ort
...
...
I
6 XML Remote Procedure Calls
232
Server
(XML~RPC)
lmport arg. apache xmlrpc server. PropertyHandl erMapPlng;
lmport arg. apache xmlrpc server.XmlRpcServer;
lmport arg. apache xmlrpc webserver.WebServer;
public class Server (
public static voi d main(String[J args) throws Except i on (
int port ~ Int eger .parsel nt (args[ OJ ) ;
PropertyHandlerMapping phm ~ new Property HandlerMapping();
phm. add Handl er( "personQuery". PersonQueryHandl er. cl ass) ;
W
ebServer webServer = new WebServer(port);
Xml RpcServer ser ver = webServer.getXmlRpcServer();
server.setHandlerMapping(phm);
webServer.start();
Client
l mpor t ja va net. URL;
l mpor t ja va util.Map;
l mport arg. apache xmlrpc cllent Xm1RpcCl i ent :
l mport arg. apache xmlrpc cllent Xm1RpcCl i entConfigImp 1;
public class Cli ent (
@Suppress Warnings("unchecked" )
public static voi d main(String args[J) throws Except i on (
URL url ~ new URL(args[OJ);
String name ~ args[ IJ;
Xml RpcCli entCon fig Impl config ~
new Xm1RpcCl i entConfi 9Imp1( ) ;
confi g. setServerURl(ur1);
Xml RpcCl i ent client ~ new XmlRpcClient();
client.setConfig(config);
Object[J params
~
Object resu lt
c II ent. execute( "personQuery. getPersonen" ,
=
( name );
params) ;
if (result !~ null) (
for (Object obj
(Object[J) resul t ) (
Person pers = PersonHelper.fromMap(
(Map<String. Object» obj);
pers.print();
System.out.println();
6.4 Dynamische Proxies
233
Hier wird jede Map des von execute gelieferten Arrays in ein
Person-Objekt transformiert.
6.4
Dynamische Proxies
Mit Hilfe so genannter dynamischer Proxies kann die Programmierung des Clients vereinfacht werden. Ein dynamischer Proxy
ist eine Klasse, die erst zur Laufzeit dynamisch generiert wird.
Ziel ist es , die entfernte, vom Server implementierte Methode wie
eine "normale" lokale Methode auf der Clientseite aufzurufen.
Dazu muss zunächst die Handler-Klasse ein Interface implementieren. Dasselbe Interface wird auch vom Client verwendet.
Wir erläutern die Vorgehensweise anhand des Echo-Dienstes aus Programm 6.5
Programm 6.1.
public inter face Echo (
String get Echo(String s);
String get EchoWithDate(String s);
Interface Echo
lmport java text Slmpl eDateFormat;
Implementierung
Echolmpl
lmport java.utll.Date;
public class Echolmpl implements Echo
public String get Echo(String s) (
return 5;
public Str ing get EchoWithDate(String s) (
Simpl eDateFormat f ~ new Simpl eDateFormat(
"dd.l+1.yyyy HH ;mm;ss ") ;
return "[" + f.format(new Date()) + "J " + s;
Der beim Aufruf der Methode add Hand ler benutzte Key für die
Handler-Klasse muss mit dem voll qualifizierten Namen des Interfaces (Paket .Interface) übereinstimmen.
6 XML Remote Procedure Calls (XML~RPC)
234
EchoServer
lmport arg. apache xmlrpc server. PropertyHandl erMapPlng;
lmport arg. apache xmlrpc server.XmlRpcServer;
lmport arg. apache xmlrpc webserver.WebServer;
public class EchoServer (
public static void main(String[] args) throws Exception (
int port ~ Integer.parselnt(args[O]);
PropertyHandlerMapping phm ~ new PropertyHandlerMapping();
phm.addHandler("Echo". Echolmpl.class);
WebServer webServer = new WebServer(port);
XmlRpcServer server = webServer.getXmlRpcServer();
server.setHandlerMapping(phm);
webServer.start();
Auf der Clientseite wird nun nicht das Xm 1RpcCl i ent-Obiekt
cl i ent direkt zum Aufruf der entfernten Methode benutzt, sondem zunächst eine Instanz der Klasse
arg. apache. xm1rpc cII ent. ut i l .Cll entF actory
wie folgt erzeugt
ClientFactory factory
~
new ClientFactory(client);
Die Cl i ent.Factory-Methode
Object newlnstance(Class c)
wird mit dem Interface Echo. cl ass als Argument aufgerufen.
Zurückgeliefert wird eine Implementierung dieses Interfaces, die
intern cII ent nutzt, um die entfernte Methode aufzurufen:
Echo echo
~
(Echo) factory.newlnstance(Echo.class);
Nun können die
Stri ng s
EchoClient
~
Echo~Methoden
aufgerufen werden, z: B.
echo.getEcho ("Ha 11 0");
import java.net.URL;
import org.apache xmlrpc client XmlRpcClient;
import org.apache xmlrpc client XmlRpcClientConfiglmpl;
lmport arg. apache xmlrpc cllent utll .CllentFactory;
public class EchoClient (
public static void main(String args[]) throws Exception (
URL url ~ new URL(args[O]);
XmlRpcClientConfiglmpl config ~
new Xm1RpcCl i entConfi 9Imp1( ) ;
6.5 Filterung von IP-Adressen
config.setServerURL(url);
XmlRpcClient client ~ new XmlRpcClient();
client.setConfig(config);
ClientFactory factory ~ new ClientFactory(client);
Echo echo ~ (Echo) factory.newlnstance(Echo.class);
String s ~ echo.getEcho("Hallo");
System.out.println(s);
String t ~ echo.getEchoWithOate("Hallo");
System.out.println(t);
6.5
Filterung von IP-Adressen
Mit den folgenden WebServer-Methoden kann der Webserver nur
bestimmten Clients den Methodenaufruf gestatten bzw, verbieten.
void setParanoid(boolean p)
schaltet die Filterung von IP-Adressen ein (true) bzw, aus
(false). Der Webserver akzeptiert standardmäßig Anfragen aller
IP-Adressen. Mit true wird zunächst keine Client-Verbindung
akzeptiert.
void acceptClient(String address)
fügt die IP-Adresse address in die Liste der akzeptierten IPAdressen ein. address kann * enthalten, um einen Nummernkreis
anzugeben (z. B. 194.94 .124.*). acceptCl ient wirkt nur, wenn
setParanoid(true) aufgerufen wurde.
void denyClient(String address)
fügt die IP-Adresse address in die Liste der ausgeschlossenen IPAdressen ein. address kann * enthalten, um einen Nummernkreis
anzugeben (z. B. 194.94.124.*). denyClient wirkt nur, wenn
setParanoid(true) aufgerufen wurde.
Das folgende Beispiel zeigt den Einsatz der IP-Filterung. Die Programm 6,6
Liste der akzeptierten (accept) bzw. ausgeschlossenen (deny) IPAdressen wird der Property-Datei ip.txt entnommen.
Beispiel,
accept; 127.0.0.110.108.105.* 10.108.1.51
235
236
EchoServer
6 XML Remote Procedure Calls
(XML~RPC)
lmport java lo.FlleInputStream;
lmport java util.Properties;
import java util.StringTokenizer;
import arg. apache xmlrpc server. PropertyHandl erMapping;
import arg. apache xmlrpc server.XmlRpcServer;
import arg. apache xmlrpc webserver.WebServer;
public class EchoServer (
public static void main(String[] args) throws Exception (
int port ~ Integer.parselnt(args[O]);
PropertyHandlerMapping phm ~ new PropertyHandlerMapping();
phm.addHandler("echo". Echo.class);
WebServer webServer = new WebServer(port);
webServer setParanoid(true);
FilelnputStream in ~ new FilelnputStream("ip txt");
Properties ip = new Properties();
ip.load(in) ;
in.close();
Stri ng addr ~ i p. getProperty(" accept");
if (addr !~ null) (
StringTokenizer st = new StringTokenizer(addr, " ");
while (st.hasMoreTokens())
webServer.acceptClient(st.nextToken());
add r ~ i P.getProperty ("deny" ) ;
if (addr !~ null) (
StringTokenizer st = new StringTokenizer(addr, " ");
while (st.hasMoreTokens())
webServer.denyClient(st.nextToken());
XmlRpcServer server = webServer getXmlRpcServer();
server.setHandlerMapping(phm);
webServer.start();
Das Client-Programm löst eine Ausnahme aus, wenn die IPAdresse des Client-Rechners nicht zugelassen ist.
6.6 Einbettung von XML-RPC in Apache Tomcat
6.6
237
Einbettung von XML-RPC in Apache Tomcat
Bisher "WUrde in allen Beispielen die Klasse WebServer benutzt.
Sie implementiert einen einfachen HTTP-Server, der zudem XMLRPC-Anfragen verarbeiten kann. Bisweilen ist es aus Performance- und Sicherheitsgründen erforderlich. die XML-RPC-Verarbeitung in einen anderen, marktgängigen Webserver zu integrieren.
Wir zeigen diese Integration am Beispiel von Apache Tomcat, Programm 6.7
der einen Servlet-Container als Ablaufumgebung für Webanwendungen bietet. Im Folgenden setzen wir den Umgang mit Tomcat
und Grundlagen zur Servlet-Technologie voraus.
Die Klasse
arg. apache xmlrpc.webserver.XmlRpcServletServer
ist Subklasse von Xm1RpcServer und besitzt die folgende Methode,
void execute(javax.servlet .http.HttpServletRequest.
javax.servlet.http.HttpServletResponsel
throws javax.servlet.ServletExceptlon, java.lo. IOExceptlon
Sie verarbeitet die XML-RPC-Anfrage und liefert die XML-RPCAntwort ZUlÜCk.
Diese Methode kann nun sehr einfach in der doPost-Methode
eines Servlets genutzt werden.
Die folgende abstrakte Klasse xmlrpc.AbstractXmlRpcSer vlet dient
als universelle Basisklasse für eigene Servlets.
package xmlrpc;
lmport java.lo.IOExceptlon;
lmport
lmport
import
import
import
javax.servlet
javax.servlet
javax.servlet
javax.servlet
javax.servlet
ServletConflg;
ServletExceptlon;
http.HttpServlet;
http.HttpServletRequest;
http.HttpServletResponse;
import org apache xmlrpc Xml RpcExcepti on;
lmport org apache xmlrpc server.PropertyHandlerMapplng;
lmport org apache xmlrpc webserver.XmlRpcServletServer;
public abstract class AbstractXmlRpcServlet extends
HttpServlet
AbstractXmlRpcServlet
6 XML Remote Procedure Calls (XML~RPC)
238
prlvate XmlRpcServletServer server;
public final void init(ServletConfig config)
throws ServletException {
5 uper . i m t (canf i g) ;
try (
PropertyHandlerMapping phm ~
new PropertyHandlerMapping();
addHand 1ers (phm) ;
server = new XmlRpcServletServer();
server.setHandlerMapping(phm);
catch (XmlRpcException e) (
throw new ServletException(e);
public abstract void addHandlers(PropertyHandlerMapping phm)
throws XmlRpcException;
public final void doPost(HttpServletRequest request.
HttpServletResponse response) throws ServletException,
IOExcepti on (
server.execute(request, response);
Zur Demonstration ist die Klasse echo. EchoServ let von dieser ab-
strakten Klasse abgeleitet. Sie implementiert die Methode
addHandlers. Somit haben Subklassen nur noch die Aufgabe, die
geforderten Handler zu registrieren.
EchoServlet
package echo;
import arg. apache xmlrpc.XmlRpcException;
import arg. apache xmlrpc server. PropertyHandl erMapping;
import xmlrpc.AbstractXmlRpcServlet;
public class EchoServlet extends AbstractXmlRpcServlet
public voi d addHandlers(PropertyHandlerMapping phm)
throws XmlRpcException (
phm.addHandler("echo". Echo.class);
6.6 Einbettung von XML-RPC in Apache Tomcat
239
Die hier verwendete Klasse echo. Echo entspricht der in Programm 6.1 verwendeten Klasse. Der einzige Unterschied ist , dass
eine package-Klausel hinzugekommen ist.
Die übersetzten Klassen Abstra ctXm1RpcSe r v1et, EchoServ l et und
Echo gehören gemäß ihrer Paketstruktur in das Verzeichnis
classes unter WEB-INF. Die für XML-RPC erforderlichen JARDateien müssen in das Verzeichnis lib unter WEB-INF kopiert
werden.
Die Deskriptordatei web.xml hat den folgenden Inhalt
<?xml ve rs l on="l . O" encodlng=" ISO-8859-1"?>
<web-app xmlns=''http://java.sun.com/xml/ns/javaee''
xm lns:xsi~''http://www.w3.org/2001/XHLSchema-instance''
XSl :schemaLocatl on-" http ://java.s un.com/xml/ns/ j avaee
http://java.sun . coml xm 1Insl j avaee/web- app_2_5. xsd"
verslon="2.5">
<serv 1et >
<ser vlet name>EchoServlet</s ervlet-name>
<servlet class>echo.EchoSer vlet</servlet-class>
</ serv 1et >
<ser vlet-mapplng>
<ser vlet ~name >EchoServlet</servlet name>
<url-pattern>/echo</url-pattern>
</servlet-mapplng>
</web-app>
Zum Schluss muss diese Webanwendung Tomcat bekannt gemacht werden. Wir zeigen eine von mehreren Möglichkeiten.
Die Datei xmlrpc.xml mit dem Inhalt
<Context dccßas e-" C: I ... IProg0607lserver" re 1oadab1e- "true" I>
wird in das Verzeichnis
<CATAL INA HOHE>l conf l Cat al i nal l ocal hos t
kopiert. <CATALlNA_HOHE> bezeichnet das Installationsverzeichnis
von Tomcat.
Nun kann Tomcat gestartet werden.
Der Client EchoCl i ent entspricht der gleichnamigen Klasse in
Programm 6.1.
URL ist hier, http://l oca1host :80801 xm1rpcl echo
web.xml
6 XML Remote Procedure Calls (XML~RPC)
240
Baste
Authentication
Apache XML~RPC unterstützt auch die Authentifizierung mit Benutzemame und Passwort. XML~RPC nutzt das HTIP~Verfahren
Basic Authentication (http://www .i etf. org/ rfc/ rfc2617. txt),
Die Klasse Xm1RpcCl i entConfi 9Imp1 bietet hierzu die Methoden
voi d setBasicUserName(Strlng user) und
vOld setBas;cPassword(Strlng password).
user und password werden nach dem Base64-Verfabren codiert
(siehe Kapitel 6.2) und in einem Basic Authentication Header
per HTIP an den Server geschickt. Zu beachten ist, dass es sich
hierbei um keine echte Verschlüsselung von Benutzemame und
Passwort mit dem Ziel der Geheimhaltung handelt.
Beispiel: Ist der User "hugo" und das Passwort "oguh", so lautet
der
HTIP~Header,
Authorization: Basic aHVnbzpvZ3Vo
"aHVnbzpvZ3Vo" wird nach dem Baseö-l-Verfahren
Serverseite zu "hugo: oguh" decodiert.
Programm 6.8
import java.net.URL:
EchoClient
import org.apache xmlrpc client XmlRpcClient:
import org.apache xmlrpc client XmlRpcClientConfiglmpl:
auf
der
public class EchoClient {
public static void main(String args[Jl throws Exception (
URL url ~ new URL(args[OJl:
String user ~ args[IJ:
String password ~ args[2J:
XmlRpcClientConfiglmpl config
new XmlRpcClientConfiglmpl(l:
config.setServerURL(urll:
conflQ.setBaslcUserName(user);
conflQ.setBaslcPassword(password);
XmlRpcClient client ~ new XmlRpcClient(l:
client.setConfig(configl:
Object[J params ~ { "Hallo" }:
String s ~ (Stringl client.execute(
"echo. getEcho". params 1:
System.out.println(sl:
String t
~
(Stringl client execute("echo.getEchoWithOate".
params) ;
System.out.println( tl:
6.6 Einbettung von XML-RPC in Apache Tomcat
241
Tomcat muss für die Authentifizierung konfiguriert werden. Die
Deskriptordatei web.xml ist wie folgt zu ergänzen:
<?xml vers l on="l . O" encodlng=" ISO-8859-1"?>
<web-app xmlns=''http://java.sun.com/xml/ns/javaee''
xmlns:xsi~''http://www.w3 .org/200 1/ XMLSchema-ins tance''
XSl :schemaLocatl on-" ht tp ://java .sun .com/xml/ns/ j avaee
http://java.sun.com/xml/ns/javaee/web-app 2 5 xsd"
verslon="2.5">
<serv 1et>
<ser vlet name>EchoServlet</servlet-name>
<ser vlet class>echo.EchoServlet</ser vlet cl ass>
</ serv 1et>
<ser vlet-mapping>
<servlet -name>EchoServlet</servlet name>
<url-pattern>/echo</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web -resource-name>echo</web-resource name>
<url -pattern>/echo</url-pattern>
</web-resource-collectlon>
<auth-constralnt>
<role-name >user</role name>
</auth-constralnt>
</securlty-constralnt>
<logln-conflg>
<auth-method>BAS IC</auth method>
<realm-name>Test</realm name>
</l Ogl n-confl g>
<securl ty- rol e>
<role-name>user</role name>
</securlty-role>
</web- app>
Die zugriffsberechtigten User werden der Einfachheit halber in
der XML-Datei
<CATAL INA HOME>l conf l t omcat users.xml
erfasst:
<tomcat users>
<rol e rol ename="user"/>
<user username=" hugo" password=" oguh" rol es="user" />
</torrcat-users>
web.xml
6 XML Remote Procedure Calls
242
(XML~RPC)
Zusätzlich kann eine Verschlüsselung mit Server-Authentifizierung genutzt werden. Secure Socket Layer (SSL) ist ein weit
verbreitetes Verfahren. das auf Public-Key-Verschlüsselung basiert.
Programm 6.9
Zur Nutzung von SSL müssen Server und Client vorbereitet werden. Der Quellcode der Programme ist der gleiche wie in Programm 6.8.
Konfiguration
des Servers
Zunächst wird ein Schlüsselpaar (private key, public key) mit
einem "self-signed" Zertifikat erzeugt. Dies reicht für Testzwecke
aus. Anschließend wird das Zertifikat (mit dem öffentlichen
Schlüssel des Servers) exportiert.
createKeystore
Wir benutzen hierzu das
JDK~Tool
keytool.
keytool -genkey -dname "CN~localhost. OU~FB 08. ü-Hcchschul e
Niederrhein. C~DE" -alias tomcat -keyalg RSA -validity 60
-keystore keystore -keypass tomcat -storepass tomcat
keytool -export -rfc -allas tomcat -flle tomcat.cer
-keystore keystore -storepass tomcat
(Die beiden Kommandos sind im Eingabeaufforderungsfenster
jeweils in einer Zeile einzugeben.)
server.xml
In <CATALINA HOME>lconflserver .xml wird nun ein Konnektor für
den Port 8443 eingerichtet
<Connector
port~"8443" protocol~"HTTP/1.1" SSLEnabled~"true"
maxThreads=" 150" scherre=" https" secure="t rue"
cl i ent/urth-" fa 1se" ss 1Protoco 1~"TLS"
keystoreFi 1e-" ... IProg0609lkeystore"
keystorePass="tomcat"
/>
Konfiguration
des Client
Auf der Clientseite
truststore importiert.
wird
das
Zertifikat in
den
Speicher
createTruststore
keytool -lmport -noprompt -allas tomcat -flle tomcat.cer
-keystore truststore -storepass catallna
(Das Kommando ist im Eingabeaufforderungsfenster in einer
Zeile einzugeben.)
6.7
Nutzung einer Erweiterung in Apache XML-RPC
243
Angaben zum Speicherort des Zertifikats werden beim Aufruf des Aufruf des Client
Client-Programms als Systemeigenschaften mitgegeben,
java -Djavax.net.ssl.trustStore=truststore
-Djavax.net.ssl.trustStorePassword=catallna
-cp bin;%XMLRPC_PATH% EchoClient
https;//localhost;8443/xmlrpc/echo hugo oguh
(Das Kommando ist im Eingabeaufforderungsfenster in einer
Zeile einzugeben.)
Zu beachten ist, dass statt http hier https (HTIP over SSL) anzugeben ist.
6.7
Nutzung einer Erweiterung in Apache XML-RPC
In diesem Kapitel gehen wir auf die in Kapitel 6.1 angesprochene Erweiterung in Apache XML-RPC 3.1.3 ein. Dazu muss auf
beiden Seiten (Client und Server) Apache XML-RPC zum Einsatz
kommen.
Da Apache XML-RPC in der Voreinstellung die offizielle XMLRPC-Spezifikation erfüllt, müssen für die Nutzung der nicht dem
Standard entsprechenden Erweiterung sowohl der Client als auch
der Server besonders konfiguriert werden. Hierzu ist die Eigenschaft enab1edForExtensl ans zu setzen.
Programm 6.10 zeigt, dass Klassen, die das Interface java.lo. Programm 6.10
Seri al izab 1e implementieren, als Datentypen für XML-RPCHandler verwendet werden können. Zudem nutzen wir die Programmiertechnik aus Kapitel 6.4 ("Dynamische Proxies"). Die
fachliche Aufgabenstellung wurde aus Programm 6.3 übernommen.
publlc class Posltlon lmplements java.lo.Serlallzable {
prlvate int td:
prlvate Strlng name;
prlvate double prels;
prlvate lnt me nge ;
public Position()
}
publlc Posltlon(lnt ld, Strlng name, double prels,
i nt menge) {
this.id~id;
thlS.name = name;
thls.prels = prels;
Klasse
Position
6 XML Remote Procedure Calls
244
thls. menge
=
me nge ;
public int getld()
return l d:
public void set ld(int id) (
th is.id ~ id;
publ i c String getName() (
return name;
public void setName(String name) (
thlS name = name;
public double getPreis()
return prel 5;
public void setPreis(double preis) (
thlS prels = prels;
public int getMenge()
return me nge ;
publ i c void setMenge( int me nge )
thls. menge = me nge ;
Interface
Warenkorb
Implementierung
Warenkotblmpl
lmport ja va.utll .Vector;
public interface Warenkorb (
boolean addPosltlon(Posltlon pos);
Vector<Posltlon> getPosltlonen();
lmport ja va.util .Vector;
public class Warenkor blmpl implements Warenkorb
private static Vector<Position> korb =
new Vector<Position>();
(XML~RPC)
6.7
Nutzung einer Erweiterung in Apache XML-RPC
245
public boolean addPosition (Position pos) (
korb.add (pos);
return true;
publlc Vector<Posltion> getPositionen()
return korb;
Mit Hilfe der Klasse
org.apache.xmlrpc server.XmlRpcSer verCon fig Impl
kann ein Xm1RpcServer-Objekt konfiguriert werden.
Die Xm1RpcServerConfi gImp l-Methode
void setEnabledForExtensions(boolean ext)
aktiviert (true) bzw. deaktiviert (fa 1se) die Erweiterungen.
Mit dem Aufruf der Xml RpcServer-Methode s etConfig wird die
Konfiguration confi 9 für den Server gesetzt s etConfi 9 (confi g).
import
import
import
import
org
org
org
org
apache
apache
apache
apache
xm 1rpc s erver. PropertyHandlerMapping;
xm 1rpc s erver. XmlRpcServer;
xm 1rpc s erver. Xm1RpcServerConfi g Imp1 ;
xm 1rpc webserver.WebServer;
public class Server (
public static void main(String[] args) throws Except i on (
int port ~ I nt eger .par sel nt( ar gs [ O] ) ;
PropertyHandlerMapping phm ~ new PropertyHandlerMapping();
phm. addHandler (" Warenkorb" . War enkor bl mp1. cl ass) ;
WebServer webServer
XmlRpcServer server
=
=
new WebServer(port);
webServer.getXmlRpcServer();
XmlRpcSer verConfig lmpl config ~
new XmlRpcSer verConfig lmpl();
config.setEnabledForExtensionsetrue);
server.setConfig(config);
server.set HandlerMapping(phm);
webServer.start();
Server
246
6 XML Remote Procedure Calls
(XML~RPC)
Die Xm1RpcCl i ent Conf i 9 Imp1-Methode
vOld setEnabledForExtensions(boolean ext)
aktiviert (true) bzw. deaktiviert (fa 1se) die Erweiterungen.
Client
import ja va.net.URL;
lmport ja va.utll .Vector;
import org.apache xmlrpc client Xml RpcCl i ent ;
import org.apache xmlrpc client Xml RpcCl i ent Conf i glmpl ;
l mport arg. apache xmlrpc cllent ut il .Cli ent Factory;
public class Cli ent (
public static voi d main(String args[]) throws Except i on (
URL url ~ new URL(args[O]);
XmlRpcCl i entCon fig lmpl config ~
new Xm1RpcCl i entConfi 9Imp1( ) ;
confi q. setServerURl(ur1);
config.setEnabledForExtensionsetrue);
XmlRpcCl i ent cli ent ~ new XmlRpcClient();
client.setConfig (config);
ClientFactory factory ~ new ClientFactory(client);
Warenkorb korb ~ (Warenkorb) factory
.newInstance( Warenkorb.class);
Position posl ~ new Position(lOOO. "Hamrrer". 2.5. 10) ;
korb.addPosition(pos l);
Position pos2 ~ new Position(lO lO. "Zange". 3.99. 8);
korb.addPosition(pos2);
Vector<Position> v = korb getPositionen();
for (Position pos v) (
+ pos .getld());
System.out.println(" ld;
System.out.println("Name; " + pos. getNarre ());
+ pos.getPreis());
System.out.println("Preis;
System. out .pri nt 1n( "Menge; " + pos. getMenge () ) ;
System.out .println();
6.8 Aufgaben
6.8
247
Aufgaben
1.
Entwickeln Sie einen XML-RPC-SelVer. der an ihn übermittelte Nachrichten (Username, Text) in einer Datei speichert.
2.
Entwickeln Sie einen XML-RPC-Server, der Adressen in einer
Datenbank verwaltet.
id nachname
1 Mei er
vorname
Hug o
telef on
ema i l
02 102 /1 12233
h . mei er @xy z . de
Der Client kann
•
einen Eintrag erfassen:
boolean add(String nachname, String vor name ,
String telefon. String email)
•
einen Eintrag löschen:
boolean remo ve(int id)
•
alle Einträge auflisten:
Objecte] getA11 ()
•
Einträge suchen:
Objecte] lookup(String spalte. String must er)
Die einzelnen Funktionen sollen parametergesteuert (Aufrufparameter) abgerufen werden können:
3.
Funktion
Parameter
getA 11
<ur I>
re lTDve
<ur I> <id>
lookup
<ur I> <spalte> <muster>
add
<ur I>
<nachname> <vorname> <telefon> <email>
Entwickeln Sie als Ergänzung zur Aufgabe 2 einen Client
mit grafischer Oberfläche. mit dem nach Adressen gesucht
werden kann Clookup).
XML Remo t e Procedure Calls (XM"L-RPC)
6
248
B ild 6.6 :
/jd Ad~=
Adressen suchen
nactowone 1.. IIM
'lb
N~chn~m
" Iu sen
" eier
4.
_00
1..-
11
Vornam e
T,, ~ron
021111 2JH6
02102111 2233
--
I
..
E·IU,i
anz.maassen
hme,.'
Gl
bc.de
Ein iQ.-1L-RPC-5erve r so ll Eintritts- und Austrittsze iren in
eine r Daten bank ve rwalten.
ende
id
begin n
hugo
hugo
2010 -02 -2 1 08 :01 :43 20 10-02 -2 1 12 :15 :43
2010 -02 - 2 1 13 :10 : 12 20 10 -02 -2 1 15 :45 :02
hu go
2010 - 0 2-2 1 1 6 : 25 : 11
Der dient kann
•
Beginn. und Ende zu einer ID erfassen :
Str ing setTime(Str i ng id)
•
die Ges amt zeit zu ein er ID abfragen:
St r i ng getTot alTi me (St ring i d)
5.
Dies e Aufgabe setzt PHP-Gru n dken n tnisse vo raus . Für die
vier in Aufgabe 2 implementierten :x:ML-RPC-Dienste add,
reno ve, getA11 und l ookup soll eine w eb-Obe rfläche mit Hilfe von PHP realisiert werden. Die einzelnen PHP-Skripte implem entieren also XML-RPC-Clien ts und gen erieren HTMLSeiten zur Anzeige de r Ergebnisse .
Zur Lösung dieser Aufgabe kann beispie lsweise di e XMLRPC-llbrary für PHP unte r der Adresse
ht tp: / /sourc eforge. netlpr oj ec t s /phpxm1rpc!
heruntergela den werden. Die Include -Darei xmlrpc.inc e nt-
hält den Code für die Clieru -Funktionallrät.
Herunterladen