/[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.19 - (show annotations)
Mon Jun 7 16:56:45 2004 UTC (20 years, 2 months ago) by joko
Branch: MAIN
Changes since 1.18: +14 -4 lines
enabled caching-layers
minor fixes regarding caching

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

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