/[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.13 - (show annotations)
Fri Apr 4 17:38:03 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.12: +46 -24 lines
modifications regarding error-/exception-handling and -tracing

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

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