Zend_XmlRpc_Server.xml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 14978 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="zend.xmlrpc.server">
  5. <title>Zend_XmlRpc_Server</title>
  6. <sect2 id="zend.xmlrpc.server.introduction">
  7. <title>Einführung</title>
  8. <para>
  9. <classname>Zend_XmlRpc_Server</classname> ist als vollständiger XML-RPC Server geplant,
  10. der den <ulink url="http://www.xmlrpc.com/spec">Spezifikationen auf
  11. www.xmlrpc.com</ulink> folgt. Des Weiteren implementiert er die
  12. Methode system.multicall(), welche dem Entwickler erlaubt, mehrere
  13. Anfragen aufzureihen.
  14. </para>
  15. </sect2>
  16. <sect2 id="zend.xmlrpc.server.usage">
  17. <title>Grundlegende Benutzung</title>
  18. <para>
  19. Ein Beispiel der grundlegendsten Benutzung:
  20. </para>
  21. <programlisting role="php"><![CDATA[
  22. $server = new Zend_XmlRpc_Server();
  23. $server->setClass('My_Service_Class');
  24. echo $server->handle();
  25. ]]>
  26. </programlisting>
  27. </sect2>
  28. <sect2 id="zend.xmlrpc.server.structure">
  29. <title>Server-Struktur</title>
  30. <para>
  31. <classname>Zend_XmlRpc_Server</classname> ist aus einer Vielfalt von Komponenten zusammengesetzt,
  32. die vom Server selbst über Anfrage-, Antwort- und bis hin zu Fehler-Objekten
  33. reicht.
  34. </para>
  35. <para>
  36. Um den <classname>Zend_XmlRpc_Server</classname> zu erstellen, muss der Entwickler dem Server
  37. eine oder mehrere Klassen oder Funktionen durch die Methoden
  38. <code>setClass()</code> und <code>addFunction()</code> hinzufügen.
  39. </para>
  40. <para>
  41. Wenn dieses erstmal erledigt wurde, kann man entweder der Methode
  42. <classname>Zend_XmlRpc_Server::handle()</classname> ein
  43. <classname>Zend_XmlRpc_Request</classname>-Objekt übergeben oder es wird ein
  44. <classname>Zend_XmlRpc_Request_Http</classname> instanziert, falls keines angegeben
  45. wurde - die Anfrage wird also aus <code>php://input</code> geladen.
  46. </para>
  47. <para>
  48. <classname>Zend_XmlRpc_Server::handle()</classname> versucht daraufhin, den
  49. zuständigen Handler, der durch die angeforderte Methode bestimmt wird,
  50. auszuführen. Es wird entweder ein <classname>Zend_XmlRpc_Response</classname>-
  51. oder ein <classname>Zend_XmlRpc_Server_Fault</classname>-Objekt zurückgegeben.
  52. Beide Objekte besitzen eine Methode <code>__toString()</code>, die eine
  53. valide XML-RPC Antwort im XML-Format zurückgibt, die direkt ausgegeben
  54. werden kann.
  55. </para>
  56. </sect2>
  57. <sect2 id="zend.xmlrpc.server.conventions">
  58. <title>Konventionen</title>
  59. <para>
  60. <classname>Zend_XmlRpc_Server</classname> ermöglicht es dem Entwickler, Funktionen und
  61. Methodenaufrufe als ausführbare XML-RPC Methoden anzufügen. Durch
  62. <classname>Zend_Server_Reflection</classname> wird die Überwachung aller angefügten
  63. Methoden - durch Nutzung der DocBlocks der Methoden und Funktionen
  64. werden deren Hilfstexte und Signaturen ermittelt - ermöglicht.
  65. </para>
  66. <para>
  67. XML-RPC Typen werden nicht zwingend 1:1 zu PHP-Typen konvertiert.
  68. Dennoch wird versucht, einen passenden Typ, anhand der in
  69. @param- und @return-Zeilen enthaltenen Werte, zu ermitteln. Einige
  70. XML-RPC-Typen besitzen jedoch kein direktes Äquivalent und sollten
  71. deshalb mittels PHPdoc auf einen XML-RPC-Typen hinweisen. Diese
  72. beinhalten:
  73. </para>
  74. <itemizedlist>
  75. <listitem><para>dateTime.iso8601, ein String, der das Format
  76. YYYYMMDDTHH:mm:ss besitzt</para></listitem>
  77. <listitem><para>base64, base64-kodierte Daten</para></listitem>
  78. <listitem><para>struct, jegliches assoziatives Array</para></listitem>
  79. </itemizedlist>
  80. <para>
  81. 'Anbei ein Beispiel für einen solchen Hinweis:
  82. </para>
  83. <programlisting role="php"><![CDATA[
  84. /**
  85. * Dies ist eine Beispielfunktion.
  86. *
  87. * @param base64 $val1 Base64-kodierte Daten
  88. * @param dateTime.iso8601 $val2 Ein ISO-Datum
  89. * @param struct $val3 ein assoziatives Array
  90. * @return struct
  91. */
  92. function myFunc($val1, $val2, $val3)
  93. {
  94. }
  95. ]]>
  96. </programlisting>
  97. <para>
  98. PhpDocumentor validiert keine Typen, die in Parameter- oder
  99. Rückgabewerten angegeben sind, weshalb dies keinen Einfluss auf
  100. die API-Dokumentation hat. Das Angeben der Hinweise ist notwendig,
  101. da der Server die, dem Methodenaufruf zugewiesenen, Parameter
  102. validiert.
  103. </para>
  104. <para>
  105. Es ist genauso gut möglich, mehrere Werte als Parameter oder für
  106. die Rückgabe anzugeben; die XML-RPC Spezifikation schlägt sogar
  107. vor, dass system.methodeSignatur ein Array, das alle möglichen
  108. Methodensignaturen (d.h. jegliche Kombination aus Parametern und
  109. Rückgabewerten) enthält, zurückgibt. Um dies zu erreichen, kann
  110. man, wie man es normalerweise auch beim PhpDocumentor auch tun würde,
  111. einfach den '|'-Operator nutzen.
  112. </para>
  113. <programlisting role="php"><![CDATA[
  114. /**
  115. * Dies ist eine Beispiel-Funktion.
  116. *
  117. * @param string|base64 $val1 String oder base64-kodierte Daten
  118. * @param string|dateTime.iso8601 $val2 String oder ein ISO-Datum
  119. * @param array|struct $val3 Normal indiziertes oder assoziatives Array
  120. * @return boolean|struct
  121. */
  122. function myFunc($val1, $val2, $val3)
  123. {
  124. }
  125. ]]>
  126. </programlisting>
  127. <para>
  128. Dennoch eine Anmerkung: Das Erlaubung von vielen Signaturen kann
  129. zu Verwirrung für Entwickler führen, die diese Services nutzen;
  130. man sollte einer XML-RPC Methode deshalb nur eine Signatur zuweisen.
  131. </para>
  132. </sect2>
  133. <sect2 id="zend.xmlrpc.server.namespaces">
  134. <title>Nutzen von Namensräumen</title>
  135. <para>
  136. XML-RPC besitzt ein Konzept für Namensräume; Grundlegend erlaubt es
  137. das Gruppieren von XML-RPC-Methoden durch Punkt-separierte Namensräume.
  138. Dies hilft, Namenkollisionen zwischen Methoden, die durch verschiedene
  139. Klassen offeriert werden, zu verhindern. Beispielsweise kann der
  140. XML-RPC-Server mehrere Methoden im 'system'-Namensraum nutzen:
  141. </para>
  142. <itemizedlist>
  143. <listitem><para>system.listMethods</para></listitem>
  144. <listitem><para>system.methodHelp</para></listitem>
  145. <listitem><para>system.methodSignature</para></listitem>
  146. </itemizedlist>
  147. <para>
  148. Intern werden die Methoden zu Methoden desselben Namens in der
  149. Klasse <classname>Zend_XmlRpc_Server</classname> umgeleitet.
  150. </para>
  151. <para>
  152. Um angebotenen Methoden Namensräume hinzuzufügen, muss man lediglich beim
  153. Hinzufügen der gewünschten Klasse oder Funktion einen Namensraum angeben:
  154. </para>
  155. <programlisting role="php"><![CDATA[
  156. // Alle öffentlichten Methoden in My_Service_Class sind als
  157. // myservice.METHODNAME verfügbar
  158. $server->setClass('My_Service_Class', 'myservice');
  159. // Funktion 'somefunc' ist als funcs.somefunc ansprechbar.
  160. $server->addFunction('somefunc', 'funcs');
  161. ]]>
  162. </programlisting>
  163. </sect2>
  164. <sect2 id="zend.xmlrpc.server.request">
  165. <title>Eigene Request-Objekte</title>
  166. <para>
  167. Die meiste Zeit wird man einfach den Standard-Anfragetyp
  168. <classname>Zend_XmlRpc_Request_Http</classname>, welcher im <classname>Zend_XmlRpc_Server</classname> enthalten ist,
  169. nutzen. Jedoch gibt es gelegentlich Fälle, in denen XML-RPC über die
  170. Kommandozeile (CLI), ein grafisches Benutzerinterface (GUI), eine
  171. andere Umgebung oder beim Protokollieren von ankommenden Anfragen
  172. erreichbar sein muss. Um dies zu bewerkstelligen, muss man ein
  173. eigenes Anfrage-Objekt kreieren, das <classname>Zend_XmlRpc_Request</classname> erweitert.
  174. Die wichtigste Sache, die man sich merken muss, ist sicherzustellen,
  175. dass die Methoden getMethod() und getParams() implementiert sind,
  176. so dass der XML-RPC-Server Informationen erhält, die er für
  177. das Abfertigen einer Anfrage benötigt.
  178. </para>
  179. </sect2>
  180. <sect2 id="zend.xmlrpc.server.response">
  181. <title>Eigene Antwort-Objekte</title>
  182. <para>
  183. Ähnlich wie bei den Anfrage-Objekten, kann der <classname>Zend_XmlRpc_Server</classname> auch
  184. eigene Antwortobjekte ausliefern; standardmäßig ist dies ein
  185. <classname>Zend_XmlRpc_Response_Http-Objekt</classname>, das einen passenden Content-Type HTTP-Header
  186. sendet, der für XML-RPC genutzt wird. Mögliche Nutzungen eines eigenen
  187. Objekts sind z.B. das Protokollieren von Antworten oder das Senden der
  188. Antworten zu STDOUT.
  189. </para>
  190. <para>
  191. Um eine eigene Antwortklasse zu nutzen, muss
  192. <classname>Zend_XmlRpc_Server::setResponseClass()</classname> vor dem Aufruf von <code>handle()</code>
  193. aufgerufen werden.
  194. </para>
  195. </sect2>
  196. <sect2 id="zend.xmlrpc.server.fault">
  197. <title>Verarbeiten von Exceptions durch Fehler</title>
  198. <para>
  199. <classname>Zend_XmlRpc_Server</classname> fängt die, durch eine ausgeführte Methode erzeugten,
  200. Exceptions and generiert daraus einen XML-RPC-Fehler als Antwort, wenn
  201. eine Exception gefangen wurde. Normalerweise werden die Exceptionnachrichten
  202. und -codes nicht in der Fehler-Antwort genutzt. Dies ist eine gewollte
  203. Entscheidung um den Code zu schützen; viele Exceptions entblößen mehr
  204. Informationen über den Code oder die Umgebung als der Entwickler
  205. wünscht (ein Paradebeispiel beinhaltet Datenbankabstraktion- oder
  206. die Zugriffsschichten-Exceptions).
  207. </para>
  208. <para>
  209. Exception-Klassen können jedoch anhand einer Weißliste (Whitelist) als
  210. Fehler-Antworten zurückgegeben werden. Dazu muss man lediglich die gewünschte
  211. Exception mittels <classname>Zend_XmlRpc_Server_Fault::attachFaultException()</classname> zur
  212. Weißliste hinzufügen:
  213. </para>
  214. <programlisting role="php"><![CDATA[
  215. Zend_XmlRpc_Server_Fault::attachFaultException('My_Project_Exception');
  216. ]]>
  217. </programlisting>
  218. <para>
  219. Abgeleitete Exceptions lassen sich als ganze Familie von Exceptions
  220. hinzufügen, indem man deren Basisklasse angibt. <classname>Zend_XmlRpc_Server_Exception</classname>s
  221. sind immer auf der Weißliste zu finden, da sie spezielle Serverfehler
  222. berichten (undefinierte Methoden, etc.).
  223. </para>
  224. <para>
  225. Jede Exception, die nicht auf der Weißliste zu finden ist, generiert
  226. eine Antwort mit dem '404' Code und der Nachricht 'Unknown error'.
  227. </para>
  228. </sect2>
  229. <sect2 id="zend.xmlrpc.server.caching">
  230. <title>Zwischenspeichern von Serverdefinitionen zwischen den Anfragen</title>
  231. <para>
  232. Das Hinzufügen einer Vielzahl von Klassen zu einer XML-RPC-Server Instanz
  233. kann zu einem großen Ressourcenverbrauch führen; jede Klasse muss via
  234. Reflection (<classname>Zend_Server_Reflection</classname>) inspiziert werden, welche eine Liste
  235. von allen möglichen Signaturen, die der Server verwenden kann, zurückgibt.
  236. </para>
  237. <para>
  238. Um die Einbußen zu reduzieren, kann <classname>Zend_XmlRpc_Server_Cache</classname> genutzt werden,
  239. welche die Serverdefinitionen zwischen den Anfragen zwischenspeichert. Wenn
  240. dies mit __autoload() kombiniert wird, kann es zu einem großen
  241. Geschwindigkeitsschub kommen.
  242. </para>
  243. <para>
  244. Ein Beispiel folgt:
  245. </para>
  246. <programlisting role="php"><![CDATA[
  247. function __autoload($class)
  248. {
  249. Zend_Loader::loadClass($class);
  250. }
  251. $cacheFile = dirname(__FILE__) . '/xmlrpc.cache';
  252. $server = new Zend_XmlRpc_Server();
  253. if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
  254. require_once 'My/Services/Glue.php';
  255. require_once 'My/Services/Paste.php';
  256. require_once 'My/Services/Tape.php';
  257. $server->setClass('My_Services_Glue', 'glue'); // glue. Namensraum
  258. $server->setClass('My_Services_Paste', 'paste'); // paste. Namensraum
  259. $server->setClass('My_Services_Tape', 'tape'); // tape. Namensraum
  260. Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
  261. }
  262. echo $server->handle();
  263. ]]>
  264. </programlisting>
  265. <para>
  266. Obiges Beispiel zeigt, wie der Server versucht, eine Definition
  267. aus der Datei xmlrpc.cache, welches sich im selben Ordner wie das
  268. Skript befindet, zu laden. Wenn dies nicht erfolgreich ist,
  269. lädt es die Server-Klassen, die es benötigt, und fügt sie zum
  270. Server hinzu. Danach wird versucht, die Cache-Datei mit der
  271. Serverdefinition zu erstellen.
  272. </para>
  273. </sect2>
  274. <sect2 id="zend.xmlrpc.server.use">
  275. <title>Nutzungsbeispiele</title>
  276. <para>
  277. Unten finden sich etliche Beispiele für eine Nutzung, die das
  278. gesamte Spektrum der verfügbaren Optionen für den Entwickler darstellen.
  279. These Beispiele bauen immer auf den vorangegangenen Beispielen auf.
  280. </para>
  281. <sect3 id="zend.xmlrpc.server.use.case1">
  282. <title>Grundlegende Benutzung</title>
  283. <para>
  284. Folgendes Beispiel fügt eine Funktion als ausführbare XML-RPC-Methode
  285. hinzu und verarbeitet eingehende Aufrufe.
  286. </para>
  287. <programlisting role="php"><![CDATA[
  288. /**
  289. * Gibt die MD5-Summe eines Strings zurück.
  290. *
  291. * @param string $value Wert aus dem die MD5-Summe errechnet wird
  292. * @return string MD5-Summe des Werts
  293. */
  294. function md5Value($value)
  295. {
  296. return md5($value);
  297. }
  298. $server = new Zend_XmlRpc_Server();
  299. $server->addFunction('md5Value');
  300. echo $server->handle();
  301. ]]>
  302. </programlisting>
  303. </sect3>
  304. <sect3 id="zend.xmlrpc.server.use.case2">
  305. <title>Hinzufügen einer Klasse</title>
  306. <para>
  307. Das nächste Beispiel illustriert, wie man die öffentlichen Methoden
  308. eienr Klasse als ausführbare XML-RPC-Methoden hinzufügt.
  309. </para>
  310. <programlisting role="php"><![CDATA[
  311. $server = new Zend_XmlRpc_Server();
  312. $server->setClass('Services_Comb');
  313. echo $server->handle();
  314. ]]>
  315. </programlisting>
  316. </sect3>
  317. <sect3 id="zend.xmlrpc.server.use.case3">
  318. <title>Mehrere Klassen unter der Nutzung von Namensräumen hinzufügen</title>
  319. <para>
  320. Das nächste Beispiel zeigt, wie man mehrer Klassen mit ihren eigenen
  321. Namensräumen hinzufügt.
  322. </para>
  323. <programlisting role="php"><![CDATA[
  324. require_once 'Services/Comb.php';
  325. require_once 'Services/Brush.php';
  326. require_once 'Services/Pick.php';
  327. $server = new Zend_XmlRpc_Server();
  328. // Methoden werden als comb.* aufgerufen
  329. $server->setClass('Services_Comb', 'comb');
  330. // Methoden werden als brush.* aufgerufen
  331. $server->setClass('Services_Brush', 'brush');
  332. // Methoden werden als pick.* aufgerufen
  333. $server->setClass('Services_Pick', 'pick');
  334. echo $server->handle();
  335. ]]>
  336. </programlisting>
  337. </sect3>
  338. <sect3 id="zend.xmlrpc.server.use.case4">
  339. <title>Bestimmen von Exceptions als valide Fehler-Antwort</title>
  340. <para>
  341. Im nächsten Beispiel wird gezeigt, wie man jede Exception, die von
  342. Services_Exception abgeleitet wurde, als Fehler-Antwort nutzen kann,
  343. dessen Nachricht und Code erhalten bleibt.
  344. </para>
  345. <programlisting role="php"><![CDATA[
  346. require_once 'Services/Exception.php';
  347. require_once 'Services/Comb.php';
  348. require_once 'Services/Brush.php';
  349. require_once 'Services/Pick.php';
  350. // Services_Exceptions dürfen als Fehler-Antwort genutzt werden
  351. Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
  352. $server = new Zend_XmlRpc_Server();
  353. // Methoden werden als comb.* aufgerufen
  354. $server->setClass('Services_Comb', 'comb');
  355. // Methoden werden als brush.* aufgerufen
  356. $server->setClass('Services_Brush', 'brush');
  357. // Methoden werden als pick.* aufgerufen
  358. $server->setClass('Services_Pick', 'pick');
  359. echo $server->handle();
  360. ]]>
  361. </programlisting>
  362. </sect3>
  363. <sect3 id="zend.xmlrpc.server.use.case5">
  364. <title>Nutzen eines eigenen Anfrage-Objekts</title>
  365. <para>
  366. Im folgenden Beispiel wird ein eigenes Anfrage-Objekt instanziert
  367. und durch den Server verarbeitet.
  368. </para>
  369. <programlisting role="php"><![CDATA[
  370. require_once 'Services/Request.php';
  371. require_once 'Services/Exception.php';
  372. require_once 'Services/Comb.php';
  373. require_once 'Services/Brush.php';
  374. require_once 'Services/Pick.php';
  375. // Services_Exceptions dürfen als Fehler-Antwort genutzt werden
  376. Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
  377. $server = new Zend_XmlRpc_Server();
  378. // Methoden werden als comb.* aufgerufen
  379. $server->setClass('Services_Comb', 'comb');
  380. // Methoden werden als brush.* aufgerufen
  381. $server->setClass('Services_Brush', 'brush');
  382. // Methoden werden als pick.* aufgerufen
  383. $server->setClass('Services_Pick', 'pick');
  384. // Ein neues Anfrage-Objekt wird erstellt
  385. $request = new Services_Request();
  386. echo $server->handle($request);
  387. ]]>
  388. </programlisting>
  389. </sect3>
  390. <sect3 id="zend.xmlrpc.server.use.case6">
  391. <title>Nutzen eigener Antwort-Objekte</title>
  392. <para>
  393. Das nachstehende Beispiel zeigt, wie man eine eigene Antwort-Klasse
  394. als zurückgegebene Antwort für den Server setzt.
  395. </para>
  396. <programlisting role="php"><![CDATA[
  397. require_once 'Services/Request.php';
  398. require_once 'Services/Response.php';
  399. require_once 'Services/Exception.php';
  400. require_once 'Services/Comb.php';
  401. require_once 'Services/Brush.php';
  402. require_once 'Services/Pick.php';
  403. // Services_Exceptions dürfen als Fehler-Antwort genutzt werden
  404. Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
  405. $server = new Zend_XmlRpc_Server();
  406. // Methoden werden als comb.* aufgerufen
  407. $server->setClass('Services_Comb', 'comb');
  408. // Methoden werden als brush.* aufgerufen
  409. $server->setClass('Services_Brush', 'brush');
  410. // Methoden werden als pick.* aufgerufen
  411. $server->setClass('Services_Pick', 'pick');
  412. // Ein neues Anfrage-Objekt wird erstellt
  413. $request = new Services_Request();
  414. // Nutzen eigener Antwort-Klasse
  415. $server->setResponseClass('Services_Response');
  416. echo $server->handle($request);
  417. ]]>
  418. </programlisting>
  419. </sect3>
  420. <sect3 id="zend.xmlrpc.server.use.case7">
  421. <title>Zwischenspeichern von Serverdefinition zwischen den Anfragen</title>
  422. <para>
  423. Dieses Beispiel zeigt, wie man Serverdefinitionen zwischen verschiedenen
  424. Anfragen zwischenspeichern kann.
  425. </para>
  426. <programlisting role="php"><![CDATA[
  427. // Definieren einer Cache-Datei
  428. $cacheFile = dirname(__FILE__) . '/xmlrpc.cache';
  429. // Services_Exceptions dürfen als Fehler-Antwort genutzt werden
  430. Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
  431. $server = new Zend_XmlRpc_Server();
  432. // Versucht die Serverdefinition aus dem Cache zu laden
  433. if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
  434. // Methoden werden als comb.* aufgerufen
  435. $server->setClass('Services_Comb', 'comb');
  436. // Methoden werden als brush.* aufgerufen
  437. $server->setClass('Services_Brush', 'brush');
  438. // Methoden werden als pick.* aufgerufen
  439. $server->setClass('Services_Pick', 'pick');
  440. // Speichern des Caches
  441. Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
  442. }
  443. // Ein neues Anfrage-Objekt wird erstellt
  444. $request = new Services_Request();
  445. // Nutzen eigener Antwort-Klasse
  446. $server->setResponseClass('Services_Response');
  447. echo $server->handle($request);
  448. ]]>
  449. </programlisting>
  450. </sect3>
  451. </sect2>
  452. </sect1>
  453. <!--
  454. vim:se ts=4 sw=4 et:
  455. -->