/[cvs]/nfo/php/libs/org.netfrag.glib/DesignPattern/RemoteProxy.php
ViewVC logotype

Contents of /nfo/php/libs/org.netfrag.glib/DesignPattern/RemoteProxy.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.20 - (show annotations)
Sun Jun 20 23:03:34 2004 UTC (20 years ago) by joko
Branch: MAIN
CVS Tags: HEAD
Changes since 1.19: +6 -2 lines
minor fix: don't resolve result-status as "good", if server-status contains errors

1 <?php
2 /**
3 * This file contains the DesignPattern::RemoteProxy class
4 *
5 * @author Andreas Motl <andreas.motl@ilo.de>
6 * @package org.netfrag.glib
7 * @name DesignPattern::RemoteProxy
8 *
9 *
10 */
11
12
13 /**
14 * <b>Cvs-Log:</b>
15 *
16 * <pre>
17 * -------------------------------------------------------------------------
18 * $Id: RemoteProxy.php,v 1.19 2004/06/07 16:56:45 joko Exp $
19 * -------------------------------------------------------------------------
20 * $Log: RemoteProxy.php,v $
21 * Revision 1.19 2004/06/07 16:56:45 joko
22 * enabled caching-layers
23 * minor fixes regarding caching
24 *
25 * Revision 1.18 2004/05/13 19:17:55 jonen
26 * + bugfix: utf8 conversion was missing at some backend-calls
27 *
28 * Revision 1.17 2003/07/14 10:05:23 jonen
29 * bugfix: added *needed* function 'getAttributes'
30 *
31 * Revision 1.16 2003/07/02 13:51:38 jonen
32 * removed debug dumper
33 *
34 * Revision 1.15 2003/04/11 01:32:21 joko
35 * renamed logging function
36 *
37 * Revision 1.14 2003/04/09 02:06:45 joko
38 * errormessage now shown preformatted
39 *
40 * Revision 1.13 2003/04/04 17:38:03 joko
41 * modifications regarding error-/exception-handling and -tracing
42 *
43 * Revision 1.12 2003/03/29 08:01:21 joko
44 * modified ErrorBoxing
45 *
46 * Revision 1.11 2003/03/28 06:44:51 joko
47 * VERBOSE mode
48 *
49 * Revision 1.10 2003/03/28 03:05:54 joko
50 * more fancy debugging-output
51 *
52 * Revision 1.9 2003/03/10 23:05:25 joko
53 * + fixed metadata for phpDocumentor
54 *
55 * Revision 1.8 2003/03/10 22:31:56 joko
56 * + fixed metadata for phpDocumentor
57 *
58 * Revision 1.7 2003/03/09 15:51:44 joko
59 * + additional metadata for Autodia
60 *
61 * Revision 1.6 2003/03/05 17:28:43 joko
62 * updated docu (phpDocumentor testing....)
63 *
64 * Revision 1.5 2003/03/05 17:02:22 joko
65 * updated docu (phpDocumentor testing....)
66 *
67 * Revision 1.4 2003/03/05 16:32:19 joko
68 * updated docu (phpDocumentor testing....)
69 *
70 * Revision 1.3 2003/03/05 16:10:17 joko
71 * updated docu (phpDocumentor testing....)
72 *
73 * Revision 1.2 2003/03/05 12:14:02 joko
74 * renamed method
75 * constructor argument expansion
76 *
77 * Revision 1.1 2003/03/03 22:06:46 joko
78 * refactored from Data::Driver::Proxy
79 *
80 * Revision 1.3 2003/03/01 04:56:38 joko
81 * new comment style for large blocks - stolen from phpHtmlLib
82 * commit-log encapsulated into such a block
83 * introduced some documentation
84 * refactored todo (now also inside a stylish block)
85 *
86 * Revision 1.2 2003/02/13 21:48:09 joko
87 * + caching mechanisms more configurable now
88 *
89 * Revision 1.1 2003/02/09 17:23:21 joko
90 * + refactored from flib/Application/RPC/ProxyObject.php
91 *
92 * Revision 1.7 2003/02/03 03:31:38 jonen
93 * - moved '$encoder->toISO()' to 'Remote.php'
94 *
95 * Revision 1.6 2002/12/22 13:26:20 jonen
96 * + added support of tangram independent id (e.g. Data::UUID) toggled by option at conrtuctor
97 *
98 * Revision 1.5 2002/12/18 22:36:49 jonen
99 * + added support to get remote objects via backend via 'guid'
100 * + renamed '_loadBackend' to '_loadRemote'
101 * + constructor now accepts additional options:
102 * + remote: try to get object via '_loadRemote' ...
103 * + oid: use the given identifier as an 'oid' when doing '_loadRemote'
104 * + guid: use the given identifier as a 'guid' when doing '_loadRemote'
105 *
106 * Revision 1.4 2002/12/12 02:46:31 joko
107 * + state (session) gets flushed when data was successfully loaded from remote side
108 * + fixed _loadBackend
109 * + fixed flushState
110 *
111 * Revision 1.3 2002/12/06 04:12:54 joko
112 * + replaced 'xyzCache' through 'xyzProxy'
113 * + function store(...)
114 *
115 * Revision 1.2 2002/12/05 21:44:09 joko
116 * + debugging
117 *
118 * Revision 1.1 2002/12/01 17:23:58 joko
119 * + initial check-in
120 *
121 * Revision 1.11 2002/11/12 06:05:58 cvsjoko
122 * + renamed class: Text_Encode -> TextEncode
123 *
124 * Revision 1.10 2002/10/29 19:15:33 cvsjoko
125 * - moved utf8/iso-conversion to lib/utils/Text_Encode.php
126 *
127 * Revision 1.9 2002/10/26 12:32:36 cvsjoko
128 * - removed debugging via "print"
129 * + added generic logging via PEAR::Log
130 *
131 * Revision 1.8 2002/10/22 09:51:38 cvsmax
132 * + moved semi-method 'createBackend' to method $site->user->create at User.class.php
133 *
134 * Revision 1.7 2002/10/21 18:27:09 cvsjoko
135 * - manually disabled any form of caching
136 *
137 * Revision 1.6 2002/10/17 03:48:47 cvsmax
138 * + new
139 * + function _createBackend create new Backend
140 * + function _create api-create
141 *
142 * Revision 1.5 2002/10/16 03:37:54 cvsjoko
143 * + bugfix: wrong comparison in restriction to save to array
144 *
145 * Revision 1.4 2002/10/10 03:04:23 cvsjoko
146 * + no debug-output
147 *
148 * Revision 1.3 2002/10/10 03:03:27 cvsjoko
149 * + bugfix: save object to cache only if payload from backend isn't empty
150 *
151 * Revision 1.2 2002/10/10 02:40:06 cvsjoko
152 * + new level of data-caching (session and persistant)
153 * + function _loadState loads data from session
154 * + function _saveState saves data to session
155 * + function save api-save (session & backend)
156 * + function _commit saves data to backend
157 * + handy utils
158 * + function _setAttributes
159 * + function flushState
160 *
161 * Revision 1.1 2002/10/09 00:51:39 cvsjoko
162 * + new
163 * -------------------------------------------------------------------------
164 * </pre>
165 *
166 */
167
168
169
170
171 /**
172 * Load required modules:
173 *
174 */
175 loadModule('DesignPattern::Proxy');
176
177
178 /**
179 * DesignPattern::RemoteProxy -- Multiple stage data fetching and caching
180 *
181 *
182 * This class (DesignPattern::RemoteProxy) provides an abstract framework
183 * for loading/saving arbitrary data from/to data storages interfaced
184 * by storage *proxy*-drivers.
185 * Don't mix these up with the concrete storage *handle*-drivers
186 * doing the lowlevel stuff (opening files, talking sql, etc.).
187 * These proxy-drivers are "just" wrappers around them
188 * providing a more highlevel, consistent API making
189 * it easier for Data::Driver::Proxy to do its main work:
190 *
191 * quote from: http://home.earthlink.net/~huston2/dp/proxy.html
192 * "A remote proxy provides a local representative for an
193 * object that resides in a different address space. This is
194 * what the "stub" code in RPC and CORBA provides."
195 *
196 *
197 * Multiple stage data fetching and caching:
198 *
199 * <pre>
200 *
201 * DATA, ...
202 * ... also refered to as data, should be handled as
203 * something called data.
204 *
205 * STAGES
206 * x native php4 session: serialized payload
207 * x single rdbms table: serialized payload
208 * x ... via custom database wrapper
209 * x included php-file, included php-library
210 * o object, component, package, callbacks
211 * x ... via php PEAR DB abstraction objects
212 * x remote objects (items): flat attribute hash
213 * o use Data::Object::Identity
214 * o via Data::Driver::Proxy::RPC or others
215 * o Data::Object::Encoder: encodes/decodes values
216 * o Data::Object::Interpolator: interpolates/resolves references
217 *
218 * WORK
219 * x move data transparently around between these stages
220 *
221 * TOOLS
222 *
223 * x Overview - Packages, Handlers and Stages:
224 * This is weird. ;-)
225 * - A Package can be instantiated by its namespaced classname
226 * e.g.: "mkObject('My::Package');"
227 * - A Package can be a Handler for Data::Driver::Proxy
228 * - A Handler implements exactly one Stage
229 * - Data::Driver::Proxy itself embeds these Stages:
230 * x php4-session
231 * x rdbms-table
232 * - An example Handler is Data::Driver::RPC::Remote
233 *
234 * x Handlers:
235 * x Data::Driver::RPC::Remote
236 * Interacts with a RPC::XML server (todo: talk SOAP!)
237 * using PEAR::XML::RPC.
238 * It can cache data for "disconnected mode" using the
239 * o Data::Driver::phpHtmlLib
240 * o Data::Driver::PEAR::DB
241 * x Data::Driver::PEAR::Tree (via Data::Lift)
242 *
243 * </pre>
244 *
245 *
246 * An attempt to implement some software design patterns
247 * --- RemoteProxyPattern
248 *
249 * @link http://www.agcs.com/supportv2/techpapers/patterns/papers/tutnotes/sld017.htm
250 * @link http://home.earthlink.net/~huston2/dp/proxy.html
251 * @link http://wiki.cs.uiuc.edu/PatternStories/RemoteObject
252 * @link http://c2.com/cgi-bin/wiki?ProxyPattern
253 * @link http://c2.com/cgi-bin/wiki?LazyProxies
254 *
255 * @author Andreas Motl <andreas.motl@ilo.de>
256 * @link http://www.netfrag.org/~joko/
257 *
258 * @copyright (c) 2003 - All Rights reserved.
259 * @license GNU LGPL (GNU Lesser General Public License)
260 * @link http://www.gnu.org/licenses/lgpl.txt
261 *
262 * @package org.netfrag.glib
263 * @subpackage DesignPattern
264 * @name DesignPattern::RemoteProxy
265 *
266 *
267 * @todo extend options to en-/disable caching via a) session and/or b) database
268 * make feature available via runtime setter-method to these options
269 * @todo PEAR::Cache for caching purposes!!!
270 * @todo refactor database access: use PEAR for this! no more 'connectdb' here!!!
271 * @todo make database connection more flexible to make possible
272 * to have different (probably named) proxy databases (besides a "main database")
273 * @todo rename this to Data::Proxy? or split into Data::Query, Data::Result and Data::Wrapper?
274 * @todo refactor this to a "RemoteObject" class!!! (inheriting from DesignPattern::RemoteObject)
275 * @todo rename this to "DesignPattern::LazyRemoteProxy"???
276 *
277 */
278 class DesignPattern_RemoteProxy extends DesignPattern_Proxy {
279
280 var $objectId;
281 // purpose ...
282 var $meta;
283 var $payload;
284 var $attributes;
285 var $backend;
286
287 function DesignPattern_RemoteProxy($objectId = "", $options = array() ) {
288 php::log(get_class($this) . "->new(objectId=$objectId)", PEAR_LOG_INFO);
289 global $proxy;
290
291 // 2003-03-05 - modified constructor
292 // expand objectId
293 if (is_array($objectId)) {
294 $options = $objectId[1];
295 $objectId = $objectId[0];
296 }
297
298 // trace
299 //print Dumper($objectId, $options);
300
301 // initialization/startup
302 $this->_init_meta_options($objectId, $options);
303 $this->_init_caching();
304 $this->_init_load();
305
306 //print Dumper($this);
307
308 }
309
310 function _init_meta_options( $objectId="", $options = array() ) {
311 $this->meta = $options;
312
313 if ($objectId) {
314 $this->objectId = $objectId;
315
316 // set default load mechanism
317 if ( $this->meta[remote] && ((!$this->meta[oid] && !$this->meta[guid]) || ($this->meta[oid] && $this->meta[guid])) && (!$this->meta[key]) ) {
318 $this->meta[oid] = 1;
319 }
320
321 }
322 }
323
324 function _init_caching() {
325
326 if ($this->meta[cache][session]) {
327 session_register_safe("proxy");
328 }
329
330 /**
331 * <!-- Autodia -->
332 * can do: (this is metadata supplied for Autodia, don't delete!)
333 * $this->backend = new DataSource_Proxy_XMLRPC()
334 *
335 */
336
337 if ($this->meta[remote]) {
338 //$this->backend = mkObject('Data::Driver::RPC::Remote', $this->meta[rpcinfo]);
339 $this->backend = php::mkComponent('DataSource::Proxy::XMLRPC', $this->meta[rpcinfo]);
340 }
341 }
342
343 function _init_load() {
344 if ($this->objectId) {
345 $this->load();
346 //$this->_saveProxy();
347 }
348 }
349
350
351 function load() {
352 php::log(get_class($this) . "->load()", PEAR_LOG_INFO);
353 if (!$this->_loadState()) {
354 if (!$this->_loadProxy()) {
355
356 // just load object from remote side if its flagged to be a remote one ...
357 if ($this->meta[remote]) {
358 $this->_loadRemote();
359 }
360
361 }
362 }
363 }
364
365 function save($data, $type) {
366 $this->_setAttributes($data);
367 $this->_saveState();
368 if ($type == 'commit') {
369 $this->_commit();
370 }
371 if ($type == 'create') {
372 $this->_create();
373 }
374 }
375
376 function flush() {
377 $this->flushState();
378 $this->flushProxy();
379 }
380
381 function _commit() {
382 $this->_saveBackend($this->attributes);
383 $this->flushState();
384 $this->flushProxy();
385 }
386
387
388 // TODO: make this work
389 /*
390 function _create() {
391 $this->_createBackend($this->attributes);
392 $this->flushState();
393 $this->flushProxy();
394 }
395 */
396
397 function getResult() {
398 if (!$this->meta[decoded]) {
399 $this->_decode();
400 $this->_saveState();
401 }
402 return $this->attributes;
403 }
404
405 function _setAttributes($data) {
406 $this->attributes = $data;
407 }
408
409 function getAttributes() {
410 return $this->attributes;
411 }
412
413 function flushProxy() {
414 connectdb();
415 $sql = "DELETE FROM f_proxy WHERE oid='$this->objectId'";
416 send_sql($sql);
417 }
418
419 function flushState() {
420 global $proxy;
421 unset($proxy[$this->objectId]);
422 $this->meta[decoded] = 0;
423 }
424
425 function _loadState() {
426 global $proxy;
427
428 // just do session-caching if requested
429 if (!$this->meta[cache][session]) { return; }
430
431 // trace
432 //print Dumper($this);
433
434 // debug
435 php::log(get_class($this) . "->_loadState()");
436
437 if ($this->attributes = $proxy[$this->objectId]) {
438 //print "_loadState:" . dumpVar($this->attributes);
439 $this->meta[decoded] = 1;
440 // TODO: make a parameter from this (0 deactivates session-layer)
441 return 1;
442 }
443 }
444
445 function _saveState() {
446 global $proxy;
447
448 // just do session-caching if requested
449 if (!$this->meta[cache][session]) { return; }
450
451 php::log(get_class($this) . "->_saveState()");
452 $proxy[$this->objectId] = $this->attributes;
453 //print "_saveState: " . dumpVar($this->attributes);
454 // TODO: throw exception-message back to user if operation fails
455 }
456
457 function _loadProxy() {
458
459 // FIXME!
460 if (!$this->meta[cache][db]) { return; }
461
462 // trace & debug
463 //print Dumper($this);
464 php::log(get_class($this) . "->_loadProxy()");
465
466 connectdb();
467 $sql = "SELECT payload FROM f_proxy WHERE oid='$this->objectId'";
468 if ($res = send_sql($sql)) {
469 $row = mysql_fetch_array($res, MYSQL_ASSOC);
470 if ($row) {
471 $this->payload = $row[payload];
472 // TODO: make a parameter from this (0 deactivates mysqldb-layer)
473 return 1;
474 }
475 }
476 }
477
478 // TODO: use PEAR here
479 function _saveProxy() {
480
481 // FIXME!
482 if (!$this->meta[cache][db]) { return; }
483
484 php::log(get_class($this) . "->_saveProxy()");
485 connectdb();
486 if ($this->payload) {
487 //$sql = "INSERT INTO f_proxy SET payload='$this->payload' WHERE oid='$this->objectId'";
488 $sql = "INSERT INTO f_proxy SET oid='$this->objectId', payload='$this->payload'";
489 if (!send_sql($sql)) {
490 $sql = "UPDATE f_proxy SET payload='$this->payload' WHERE oid='$this->objectId'";
491 send_sql($sql);
492 }
493 }
494 }
495
496 function _loadRemote() {
497 php::log(get_class($this) . "->_loadRemote()");
498
499 // trace
500 //print Dumper($this->meta);
501
502 // TODO: test backend for reachability first (eventually cache this information and "reset" it by another party)
503
504 // 1. check backend-handle
505 if (!$this->backend) {
506 php::log(get_class($this) . "->_loadRemote: no backend handle, please check argument 'rpcinfo'", PEAR_LOG_CRIT);
507 return;
508 }
509
510 // determine backend action by metadata
511 // check for guid or oid
512 if ($this->meta[guid]) {
513 if (!$this->objectId) {
514 php::log(get_class($this) . "->_loadRemote: argument 'guid' requires valid objectId", PEAR_LOG_WARNING);
515 return;
516 }
517 if (!$this->meta[classname]) {
518 php::log(get_class($this) . "->_loadRemote: argument 'guid' requires 'classname'", PEAR_LOG_WARNING);
519 return;
520 }
521 php::log(get_class($this) . "->_loadRemote: getObjectByGuid", PEAR_LOG_DEBUG);
522 $args = array( guid => $this->objectId, classname => $this->meta[classname] );
523 $result = $this->backend->send('getObjectByGuid', $args, array( utf8 => 1) );
524
525 } elseif ($this->meta[oid]) {
526 if (!$this->objectId) {
527 php::log(get_class($this) . "->_loadRemote: argument 'oid' requires valid objectId", PEAR_LOG_WARNING);
528 return;
529 }
530 php::log(get_class($this) . "->_loadRemote: getObject", PEAR_LOG_DEBUG);
531 $result = $this->backend->send('getObject', $this->objectId, array( utf8 => 1) );
532
533 } elseif ($this->meta[key]) {
534 if (!$this->meta[command]) {
535 php::log(get_class($this) . "->_loadRemote: argument 'key' requires 'command'", PEAR_LOG_WARNING);
536 return;
537 }
538 /*
539 if (!$this->meta[query]) {
540 php::log(get_class($this) . "->_loadRemote: argument 'key' requires 'query'", PEAR_LOG_WARNING);
541 return;
542 }
543 */
544 //php::log(get_class($this) . "->_loadRemote: $this->meta[command](" . join(' ', $this->meta[query]) . ")", PEAR_LOG_DEBUG);
545 //print Dumper(array($this->meta[command], $this->meta[query]));
546 $result = $this->backend->send($this->meta[command], $this->meta[query], array( utf8 => 1) );
547
548 }
549
550 //print "result: " . dumpVar($result) . "<br>";
551
552 $status = $this->backend->status();
553 //print Dumper($status);
554
555 $good = !$status[errors] && is_array($result) && sizeof($result) && $status[connected];
556
557 if ($good) {
558
559 // FIXME: this is dangerous!
560 /*
561 if ($_GET[debug]) {
562 print Dumper($result);
563 }
564 */
565
566 $this->payload = serialize($result);
567 // ----- move this to _encode some times
568
569 $this->_saveProxy();
570 //print "oid: $this->objectId<br>";
571 $this->flushState();
572
573 } else {
574
575 if (constants::get('APP_MODE_DEBUG')) {
576 $this->draw_error_box($status);
577 } else {
578 php::maintenance('rpc', array( status => $status ) );
579 }
580
581 }
582
583 }
584
585 function draw_error_box($status) {
586 $style = html_style("text/css", '.boxlabel_yellow { color: yellow; font-weight:bold; }');
587 $statusbox = html_div();
588 $statusbox->set_style('background: red; border: 2px black groove; width:640px; padding:10px; margin:40px;');
589 $statusbox->add( html_span('boxlabel_yellow', "Method:"), get_class($this) . "->_loadRemote", html_br() );
590 $statusbox->add( html_span('boxlabel_yellow', "Connected:"), $status[connected], html_br() );
591 $statusbox->add( html_span('boxlabel_yellow', "RPCSESSID:"), $status[RPCSESSID], html_br() );
592 foreach ($status[errors] as $error) {
593 $msg = html_pre($error[message]);
594 $statusbox->add( html_span('boxlabel_yellow', "Error($error[code]):"), $msg );
595 }
596
597 $message = "Error while talking to remote side. Please check wire, socket or api.";
598 php::log($message, PEAR_LOG_CRIT);
599 $statusbox->add( html_span('boxlabel_yellow', "Critical:"), $message, html_br() );
600
601 // V1
602 /*
603 if (constants::get('VERBOSE') || constants::get('ERRORS_ONLY')) {
604 print $style->render();
605 print $statusbox->render();
606 } else {
607 foreach ($status[errors] as $error) {
608 print Dumper($error);
609 }
610 }
611 */
612
613 // V2
614 trace( container($style, $statusbox) );
615
616 }
617
618
619 function _saveBackend($result) {
620 php::log(get_class($this) . "->_saveBackend()");
621
622 //$encoder = new TextEncode($result);
623 //$encoder->toUTF8();
624
625 // check for guid or oid
626 if($this->meta[guid]) {
627 $args = array( 'guid' => $this->objectId, 'classname' => $this->meta[classname], 'data' => $result );
628 $response = $this->backend->send('saveObjectByGuid', $args, array( utf8 => 1) );
629 }
630 if($this->meta[oid]) {
631 $response = $this->backend->send('saveObject', array('oid' => $this->objectId, 'data' => $result), array( utf8 => 1) );
632 }
633 }
634
635 function _decode() {
636 // fill attributes-hashtable from message-hashtable
637 if (!$this->payload) { return; }
638 //if ($this->attributes = $backend->decodeData($this->payload)) {
639 if ($this->attributes = unserialize($this->payload)) {
640 $this->meta[decoded] = 1;
641 }
642 }
643
644 function store($struct) {
645 $this->payload = serialize($struct);
646 $this->_saveProxy();
647 }
648
649 }
650
651 ?>

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