/[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.10 - (show annotations)
Fri Mar 28 03:05:54 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
Changes since 1.9: +34 -6 lines
more fancy debugging-output

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

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