/[cvs]/nfo/php/libs/org.netfrag.glib/DataSource/Proxy/XMLRPC.php
ViewVC logotype

Contents of /nfo/php/libs/org.netfrag.glib/DataSource/Proxy/XMLRPC.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (show annotations)
Fri Apr 4 21:23:29 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.5: +211 -65 lines
session-based rpc-communcation: revamped code (especially 'function _call')
renamed state-handling trigger-methods: now _sleep and _wakeup

1 <?
2 /**
3 * This file contains the DataSource::Proxy::XMLRPC module.
4 *
5 * @author Andreas Motl <andreas.motl@ilo.de>
6 * @package org.netfrag.glib
7 * @name DataSource::Proxy::XMLRPC
8 *
9 */
10
11
12 /*
13 ## --------------------------------------------------------------------------
14 ## $Id: XMLRPC.php,v 1.5 2003/03/28 06:56:52 joko Exp $
15 ## --------------------------------------------------------------------------
16 ## $Log: XMLRPC.php,v $
17 ## Revision 1.5 2003/03/28 06:56:52 joko
18 ## updated logging/debugging code
19 ##
20 ## Revision 1.4 2003/03/28 03:00:12 joko
21 ## enhanced error- and exception-handling
22 ##
23 ## Revision 1.3 2003/03/27 16:05:01 joko
24 ## enhanced 'function _call': debugging, tracing and autodisconnect now parametrized
25 ##
26 ## Revision 1.2 2003/03/05 23:16:48 joko
27 ## updated docu - phpDocumentor is very strict about its 'blocks'...
28 ##
29 ## Revision 1.1 2003/03/03 21:53:52 joko
30 ## refactored from Data::Driver::RPC::Remote
31 ##
32 ## Revision 1.4 2003/02/20 21:42:43 joko
33 ## + fix: always converting from utf8 into latin here (for now)
34 ##
35 ## Revision 1.3 2003/02/13 21:51:29 joko
36 ## + now can remember its connection-state
37 ## + sub ping
38 ##
39 ## Revision 1.2 2003/02/13 00:42:44 joko
40 ## +- renamed modules
41 ##
42 ## Revision 1.1 2003/02/09 17:25:38 joko
43 ## + refactored from flib/Application/RPC/Remote.php
44 ##
45 ## Revision 1.6 2003/02/03 03:37:45 jonen
46 ## - removed unused argument '$decode' at function '_call()'
47 ## + added argument '$options=array()' at function '_call()'
48 ## which will be passed to function '&decodeData()' for e.g. utf8 decoding
49 ## + added '$encoder->toISO()' at '&decodeData()', (re)moved from 'ProxyObject.php'
50 ##
51 ## Revision 1.5 2002/12/22 13:24:09 jonen
52 ## + added utf8 encoding at 'send()' toggled by option
53 ##
54 ## Revision 1.4 2002/12/19 02:02:25 jonen
55 ## + minor changes
56 ##
57 ## Revision 1.3 2002/12/19 01:59:37 jonen
58 ## + minor changes: coment debug prints
59 ##
60 ## Revision 1.2 2002/12/05 21:45:31 joko
61 ## + debugging
62 ##
63 ## Revision 1.1 2002/12/01 17:23:58 joko
64 ## + initial check-in
65 ##
66 ## Revision 1.3 2002/12/01 06:22:57 cvsjoko
67 ## + minor update: now can use config defaults or given args
68 ##
69 ## Revision 1.2 2002/10/29 19:14:45 cvsjoko
70 ## - bugfix: dont' do utf8-encoding here
71 ##
72 ## Revision 1.1 2002/10/09 00:51:39 cvsjoko
73 ## + new
74 ##
75 ##
76 ## -------------------------------------------------------------------------
77 */
78
79
80 // V1: normal require
81 //require_once 'XML/RPC/RPC.php';
82
83 // V2: first time load of foreign module (PEAR::XML::RPC)
84 //require_once 'XML/RPC/RPC.php';
85 loadModule('XML::RPC::RPC');
86
87 loadModule('Class::Logger');
88
89
90 /**
91 * This is the DataSource::Proxy::XMLRPC module.
92 *
93 * @author Andreas Motl <andreas.motl@ilo.de>
94 * @package org.netfrag.glib
95 * @subpackage DataSource
96 * @name DataSource::Proxy::XMLRPC
97 *
98 * @todo SOAP?
99 *
100 */
101 class DataSource_Proxy_XMLRPC extends Class_Logger {
102
103 var $configured;
104 var $meta = array();
105 var $response = null;
106 var $errors = array();
107
108 function DataSource_Proxy_XMLRPC($args = array()) {
109
110 parent::constructor();
111
112 //print Dumper($this, $args);
113 //print Dumper($args);
114
115 $this->_wakeup();
116
117 // force connect?
118
119 // V1
120 /*
121 if (!isset($this->meta[connected]) || $args[connect]) {
122 $this->meta[connected] = 1;
123 }
124 */
125
126 // V2
127 $this->FORCE_CONNECT = $args[connect];
128
129
130
131 // merge args - V1
132 //if ($args['Host']) { $this->HOST = $args['Host']; }
133 //if ($args['Port']) { $this->PORT = $args['Port']; }
134
135 // merge args - V2
136 // TODO: php::merge_to($this, $args);
137 //php::merge_to($this, $args);
138 //print Dumper($args);
139 if (is_array($args)) {
140 foreach ($args as $key => $val) {
141 //print "key: $key<br/>";
142 $key = strtoupper($key);
143 $this->$key = $val;
144 }
145 }
146
147 // check if host is valid
148 if (!$this->HOST) {
149 $this->_raiseException( "->constructor: attribute 'host' is empty, please check your settings");
150 return;
151 }
152
153 if (!$this->PORT) {
154 $this->_raiseException( "->constructor: attribute 'port' is empty, please check your settings");
155 return;
156 }
157
158 $this->configured = 1;
159
160 }
161
162 function _wakeup() {
163 global $Data_Driver_RPC_Remote_meta;
164 session_register_safe("Data_Driver_RPC_Remote_meta");
165 $this->meta = $Data_Driver_RPC_Remote_meta;
166 $this->_sleep();
167 }
168
169 function _sleep() {
170 global $Data_Driver_RPC_Remote_meta;
171 $Data_Driver_RPC_Remote_meta = $this->meta;
172 }
173
174 function send($command, $data = "", $options = array()) {
175
176 $this->log(get_class($this) . "->send: " . $command, PEAR_LOG_DEBUG);
177
178 /*
179 if (!$this->isConnected()) {
180 $this->_raiseException( "->send[ command=$command ]: not connected while trying to send command!");
181 return;
182 }
183 */
184
185 // do 'encode' here and ...
186 if ($options[utf8]) {
187 $encoder = new Data_Encode($data);
188 $encoder->toUTF8();
189 }
190 // call '_call' with 'decode'
191 return $this->_call($command, $data, $options);
192 }
193
194
195 function _call($command, $data = "", $options = array() ) {
196
197 $RETRY_COUNT = 0;
198 $RETRY_MAX = 1;
199
200 do {
201
202 $RETRY_COUNT++;
203
204 $this->__exception_method = '_call';
205
206 if (!$this->configured) {
207 $this->_raiseException("class not configured properly");
208 return;
209 }
210
211 // populate options list - mostly for debugging purposes
212 $options_list = array();
213 foreach ($options as $key => $value) {
214 array_push($options_list, "$key=$value");
215 }
216
217 $data_debug = $data;
218 if (is_array($data_debug)) { $data_debug = join(", ", $data_debug); }
219 $options_debug = join(", ", $options_list);
220 // FIXME: replace through '_raiseException'?
221 $this->log(get_class($this) . "->_call: " . $command . "(" . $data_debug . ") [" . $options_debug . "]", PEAR_LOG_DEBUG);
222
223 // trace
224 //print "call: $command<hr>";
225 //print Dumper($data);
226 //print Dumper($this);
227
228 // message - request
229 $msg = new XML_RPC_Message($command);
230
231 // data
232 if ($data) {
233 $data_enc = XML_RPC_encode($data);
234 $msg->addParam($data_enc);
235 }
236
237 $this->_hook_request($msg);
238
239 // ???
240 //print htmlentities($msg->serialize());
241
242 // remote procedure call
243 $rpc = new XML_RPC_Client("/", $this->HOST, $this->PORT);
244
245 // TODO: detect highlevel errors, raise proper exceptions on them (e.g. 'Unknown method', 'Method signature error(s)')
246 // done: now possible to declare tracing at this point in configuration
247 // Please look inside your {appname}/etc/hosts/{hostname}.php
248 $rpc->setDebug($this->TRACE);
249
250 //$rpc->setDebug(1);
251
252 if ( $this->response = $rpc->send($msg) ) {
253 // intermediate response checking
254 $errcode = $this->_hook_errorcheck();
255 $negotiation = $this->_hook_negotiation($errcode);
256 } else {
257 // TODO: redirect this error elsewhere!
258 //print "RPC-error!<br>";
259 $this->_raiseException("no response");
260 return;
261 }
262
263 // retry method issued right now - i.e. in case an authentication came in between
264 if (!$negotiation[retry]) {
265 break;
266 }
267
268 } while ($RETRY_COUNT < $RETRY_MAX);
269
270 // message - response
271 $response_enc = $this->response->value();
272
273 // TODO: what's this? prematurely returning here should not be considered "stable"....
274 $data = $this->decodeData($response_enc, $options);
275 $this->_hook_response($data);
276 return $data;
277
278 }
279
280
281 function &decodeData(&$payload, $options = array() ) {
282 //if (!is_object($payload)) { return; }
283 if ($payload) {
284 // data
285 $data = XML_RPC_decode($payload);
286 //print "data: " . dumpVar($response);
287
288 // decode UTF8 to ISO if wanted
289 //if ($options[to_latin]) {
290 $encoder = new Data_Encode($data);
291 $encoder->toISO();
292 //}
293
294 $this->_be_connected();
295
296 return $data;
297 } else {
298 //print "ERROR!<br>";
299 return 0;
300 }
301 }
302
303 function _be_connected() {
304 $this->meta[connected] = 1;
305 $this->_sleep();
306 }
307
308 function _raiseException($message, $code = null) {
309
310 $classname = get_class($this);
311
312 if ($this->__exception_method) {
313 $message_full = $classname . '->' . $this->__exception_method . ': ' . $message;
314 }
315
316 // aggregate errors for this run/query
317 $this->_add_error($message, $code);
318
319 // spout out the error message of the raised exception
320 $this->log($classname . $message_full, PEAR_LOG_ERR);
321
322 // handle some stuff regarding more special behaviour (will this get rule-based sometimes?)
323
324 // handle 'FORCE_CONNECT'
325 $connect_condition = $this->FORCE_CONNECT = ($this->isConnected() && $this->DISCONNECT_ON_ERROR);
326 if ($connect_condition) {
327 $message = '->_raiseException: [DISCONNECT_ON_ERROR] done transparently. Please reconnect manually.';
328 $this->_add_error($classname . $message, $code);
329 //$this->log($classname . $message, PEAR_LOG_WARNING);
330 $this->log($classname . $message, PEAR_LOG_ERR);
331 $this->meta[connected] = 0;
332 $this->_sleep();
333 }
334 }
335
336 function isConnected() {
337 return $this->meta[connected];
338 }
339
340 function ping() {
341 $this->_call('ping');
342 }
343
344
345 function _hook_negotiation($error_code) {
346
347 $result = array();
348
349 // 401 = authorization required
350 if ($error_code == 401) {
351
352 //print "<b>401</b>!!!<br/>";
353 //print Dumper($this);
354
355 // V1 - authenticate using hardcoded plaintext values (bad thing inside here!!!)
356 $auth_ok = $this->_call('authenticate', array( user => 'hello', pass => '123' ));
357 //$auth_ok = $this->_call('authenticate', array( user => 'hello', pass => '12345' ));
358
359 // authentication success? try issued method again now...
360 if ($auth_ok) {
361 $result[retry] = 1;
362 }
363
364 // V2 - sub-negotiation:
365 // "Do we already have a password supplied in cache?"
366 // Use it if we have, otherwise redirect to login page
367 // prompting for credentials for specified transport.
368 //Header("Location: " . topicLink('Login'));
369 //print "user: " . $this->USER . "<br/>";
370 //print "pass: " . $this->PASS . "<br/>";
371
372 }
373
374 // 511 = sent unknown session identifier (probably: session timed out)
375 // solution: invalidate local session in state => new session will be attempted on next request
376 if ($error_code == 511) {
377 $this->meta[session][enabled] = 0;
378 $this->_sleep();
379 }
380
381 return $result;
382
383 }
384
385 function _hook_errorcheck() {
386 // Please look inside your {appname}/etc/hosts/{hostname}.php for toggling $this->DEBUG
387 if ($error_code = $this->response->faultCode()) {
388 // raise exception
389 //print "fault-code: " . $this->response->faultCode() . "<br/>";
390 //print "fault-string: " . $this->response->faultString() . "<br/>";
391 $this->_raiseException($this->response->faultString(), $error_code);
392 return $error_code;
393 }
394 }
395
396 //if ($this->DEBUG && $error_code = $msg_response->faultCode()) {
397
398 function _add_error($message, $code) {
399 array_push( $this->errors, array(code => $code, message => $message) );
400 }
401
402 function status() {
403 $status = array(
404 connected => $this->isConnected(),
405 errors => $this->errors,
406 );
407 $status[RPCSESSID] = $this->meta[session][id];
408 return $status;
409 }
410
411
412 // TODO: introduce condition: just do if connection requires session/auth
413 function _hook_request(&$request) {
414
415 //print Dumper($request);
416
417 // ignore core methods (like 'session_id') here to prevent recursive loops!
418 // would be a bad thing on the net!
419 if (in_array($request->methodname, array('ping', 'session_id'))) { return 1; }
420
421 // calculate conditions
422 $session_requested = 1;
423 //$use_session = $this->meta[session][requested];
424 $session_running = $this->meta[session][enabled];
425 $session_error = $this->meta[session][error];
426 $init_session = $session_requested && !$session_running && !$session_error;
427
428 // 1. if session already established, add sessionid to request and return
429 if ($session_running) {
430 //trace("Session " . $this->meta[session][id] . " already initialized!" . "<br/>");
431 // append additional parameter (hash with key 'RPCSESSID')
432 // to send assigned sessionid back to server inside each request
433 $data = array( RPCSESSID => $this->meta[session][id] );
434 $data_enc = XML_RPC_encode($data);
435 //print Dumper($data_enc);
436 $request->addParam($data_enc);
437 //return 1;
438 }
439
440
441 // 2. if session is required/requested, but not yet initialized,
442 // do it to be able to propagate the sessionid transparently later
443
444 if ($init_session) {
445 static $init_count;
446 $init_count++;
447 //trace("Initializing session: $init_count" . "<br/>");
448 //exit;
449 $response = $this->_call('session_id');
450 //print "response: " . Dumper($response) . "<br/>";
451 //exit;
452
453 if ($response) {
454 $this->meta[session][id] = $response;
455 $this->meta[session][enabled] = 1;
456 } else {
457 $this->meta[session][error] = 1;
458 }
459
460 $this->_sleep();
461 //print Dumper($this->meta);
462 //exit;
463 }
464
465 //print Dumper($request);
466
467 return 1;
468 }
469
470 function _hook_response(&$data) {
471 //$status[RPCSESSID] = $this->meta[session][RPCSESSID];
472 }
473
474 }
475
476 ?>

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed