/[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.16 - (show annotations)
Wed Jul 2 13:51:38 2003 UTC (21 years ago) by jonen
Branch: MAIN
Changes since 1.15: +5 -2 lines
removed debug dumper

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

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