/[cvs]/nfo/php/libs/org.netfrag.glib/DataSource/Generic.php
ViewVC logotype

Annotation of /nfo/php/libs/org.netfrag.glib/DataSource/Generic.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations)
Wed Mar 5 17:28:43 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.1: +66 -45 lines
updated docu (phpDocumentor testing....)

1 joko 1.1 <?php
2     /**
3     * This file contains the GenericDataSource child class
4     *
5     * @author Andreas Motl <andreas.motl@ilo.de>
6 joko 1.2 * @package org.netfrag.glib
7     * @name DataSource::Generic
8 joko 1.1 *
9     */
10    
11     /**
12 joko 1.2 * $Id: Generic.php,v 1.1 2003/03/05 12:04:00 joko Exp $
13     *
14     * $Log: Generic.php,v $
15     * Revision 1.1 2003/03/05 12:04:00 joko
16     * + initial commit, from GenericDataSource
17 joko 1.1 *
18     * Revision 1.3 2003/03/03 21:24:18 joko
19     * now based on DesignPattern::RemoteProxy
20     *
21     * Revision 1.2 2003/03/02 01:05:13 joko
22     * - purged old code
23     *
24     * Revision 1.1 2003/03/01 21:47:15 joko
25     * renamed from AbstractDataSource.inc
26     *
27     * Revision 1.1 2003/03/01 20:17:15 joko
28     * renamed from GenericDataSource.inc
29     * replaced '_proxy' through '_handler' to better associate the ability to act as a generic dispatcher handler
30     *
31     * Revision 1.3 2003/03/01 15:36:11 joko
32     * + debugging
33     * + renamed some methods regarding new proposal (build_handler_options, fetch_result, etc.)
34     *
35     * Revision 1.2 2003/03/01 04:50:27 joko
36     * encapsulated all output into tracing mode
37     * disabled tracing
38     *
39     * Revision 1.1 2003/03/01 03:10:40 joko
40     * + initial commit
41     *
42     *
43     */
44    
45     /**
46     * This requires the MemoryDataSource base class
47     *
48     */
49    
50     // V1: compile-time-include, stable since years ;-)
51     //include_once($phphtmllib."/widgets/data_source/MemoryDataSource.inc");
52    
53     // V2: runtime-load proposal, more flexible
54     // loadModule('MemoryDataSource', 'inc'); // doesn't work (would do if basepath of module is in libpath)
55     // loadModule($phphtmllib . '::widgets::data_source::MemoryDataSource', 'inc'); // works (by accident)
56     // loadModule($phphtmllib . "/widgets/data_source/MemoryDataSource", "inc"); // works (should be stable)
57     // loadModule($phphtmllib . "/widgets/data_source/MemoryDataSource.inc", null); // works (should be stable)
58    
59    
60    
61     /**
62     * Have to have PEAR and DB included
63     * the pear dir must be in the
64     * include path.
65     */
66     // require_once("PEAR.php"); // FIXME: what about PEAR::XML::RPC?
67     // require_once("DB.php");
68    
69 joko 1.2
70     loadModule('DesignPattern::Proxy');
71     loadModule('DesignPattern::TransparentProxy');
72     loadModule('DesignPattern::RemoteProxy');
73    
74     //class GenericDataSource extends MemoryDataSource {
75     //class GenericDataSource extends DesignPattern_Proxy {
76     //class GenericDataSource extends DesignPattern_RemoteProxy {
77     //class GenericDataSource extends DesignPattern_TransparentProxy {
78    
79     //class GenericDataSource extends MemoryDataSource {
80     //loadModule("DataSource::Proxy::Memory");
81     //class GenericDataSource extends DataSource_Proxy_Memory {
82     //class DataSource_GenericDataSource extends DataSource_Proxy_Memory {
83     //class DataSource_GenericDataSource extends DesignPattern_Proxy {
84     //class DataSource_GenericDataSource extends DataSource_Proxy_Memory {
85    
86     // now independent!!! new class-inheritance-tree possible now!
87     // solved by having an Adapter *and* a Proxy here!
88     //class GenericDataSource {
89    
90     loadModule('DesignPattern::AdapterProxy');
91    
92    
93    
94 joko 1.1 /**
95     * This GenericDataSource child class is *completely* independent
96     * of a specific storage implementation and intended to be a wrapper
97 joko 1.2 * class on the way from phpHtmlLib to Xyz. <br>
98 joko 1.1 * ("Yyz" gets represented by Data::Storage by now.....)
99     *
100     * GenericDataSource interacts with an intermediate "proxy" object
101 joko 1.2 * (e.g. Data::Driver::Proxy) when doing queries. <br>
102     * <pre>
103 joko 1.1 * Don't mix this up with a persistent database handle which gets
104     * reused each and every time for different queries.
105     * Here *is* a new instance of Data::Driver::Proxy for *each* query.
106 joko 1.2 * </pre>
107 joko 1.1 *
108     * But the point is: Caching! The actual *data* isn't read redundant!
109     *
110 joko 1.2 * <pre>
111     * --- snipped into here from above ---
112     * that may use arbitrary code modules as database handlers.
113     * It's aims are to get together:
114     * o phpHtmlLibs "source-handlers"
115     * o PEAR's database handlers
116     * o custom ones (e.g. Data::Driver::Proxy object, which talks to a remote rpc server)
117     * --- snipped into here from above ---
118     * </pre>
119     *
120 joko 1.1 *
121     * !!!!!! refactor this to Data::Driver::Proxy !!!!!! <-----------------
122     *
123     * Data::Driver::Proxy instantiates "handlers" below itself
124     * which act as encapsulated workers to actually do the stuff to do.
125     * It can cache data for "disconnected mode" using the
126     * php PEAR DB abstraction objects.
127     * One worker already implemented is Data::Driver::RPC::Remote, which
128     * talks to a RPC::XML server (todo: talk SOAP!) using PEAR::XML::RPC.
129     *
130 joko 1.2 * <pre>
131 joko 1.1 * --- refactored here, but: redundant somehow ---
132     * Data::Driver::Proxy uses a PEAR XML::RPC object to actually
133     * talk HTTP and serialize data chunks to and from XML,
134     * but adds some caching to this "process of fetching data from a remote side".
135     * It "proxies" arbitrary data chunks a) inside a native php4 session,
136     * b) by talking to a rdbms (single proxy-table) or c) [TODO] using PEAR::Cache.
137     * --- refactored here, but: redundant somehow ---
138 joko 1.2 * </pre>
139 joko 1.1 *
140     * !!!!!! refactor this to Data::Driver::Proxy !!!!!! <-----------------
141     *
142     *
143 joko 1.2 * <b>How to use?</b>
144 joko 1.1 *
145     * Pass an array holding "locator metadata" to the constructor.
146     * GenericDataSource takes care of the rest.
147     *
148     * Pass an array to the constructor: (e.g.)
149     *
150     * 1. doing rpc-calls....
151 joko 1.2 * <code>
152 joko 1.1 * $locator = array(
153     * type => 'rpc',
154     * metadata => array( Host => 'localhost', Port => '8765' ),
155     * );
156     * $source = new GenericDataSource($locator);
157     * $this->set_data_source( &$source );
158 joko 1.2 * </code>
159 joko 1.1 *
160     * 2. [proposal] common datahandles....
161 joko 1.2 * <code>
162 joko 1.1 * $locator = array(
163     * type => 'mysql',
164     * dsn => 'known dsn markup',
165     * );
166     * $source = new GenericDataSource($locator);
167     * $this->set_data_source( &$source );
168 joko 1.2 * </code>
169 joko 1.1 *
170 joko 1.2 * @link http://www.netfrag.org/~joko/
171 joko 1.1 * @author Andreas Motl <andreas.motl@ilo.de>
172 joko 1.2 *
173     * @link http://www.netfrag.org/~jonen/
174 joko 1.1 * @author Sebastian Utz <seut@tunemedia.de>
175 joko 1.2 *
176 joko 1.1 * @copyright (c) 2003 - All Rights reserved.
177 joko 1.2 *
178     * @link http://www.gnu.org/licenses/lgpl.txt
179 joko 1.1 * @license GNU LGPL (GNU Lesser General Public License)
180     *
181     *
182 joko 1.2 * @package org.netfrag.glib
183     * @subpackage DataSource
184     * @name DataSource::Generic
185 joko 1.1 *
186 joko 1.2 * @todo
187     * <ul>
188 joko 1.1 * o mungle this to be able to be wrapped around phpHtmlLib's own storage-handles
189     * o implement another Data::Driver::Proxy container
190 joko 1.2 * </ul>
191 joko 1.1 *
192    
193     // !!!!!!!! THIS IS THE PROBLEM !!!!!!!!
194     // !!!!!!!! here is it where we have to break inheritance again !!!!!!!!
195     //
196     // THE CONFLICT: Beeing in phpHtmlLib *and* DesignPattern::TransparentProxy
197     // inheritance trees at the same time, which is *not* possible at
198     // declare-time. We *do* need some runtime-infrastructure to solve this!
199     //
200     // TODO: move build- and check-locator stuff from ObjectList to this place!!!
201     //
202     // ABOUT:
203     // 1. otherwhere: WebApp - scope:
204     // x handles page vs. block vs. widget; dispatches MVC-View
205     // 2. here: DataSource - scope:
206     // x handles bridge to frameworks (e.g. phpHtmlLib) vs. actual data driver libs (PEAR, etc.))
207     // o clean implementation using a DesignPattern::AdapterProxy
208     //
209    
210 joko 1.2 */
211 joko 1.1 class DataSource_Generic extends DesignPattern_AdapterProxy {
212    
213     // !!!!!!!! here is it where we have to break inheritance again !!!!!!!!
214     // !!!!!!!! THIS IS THE PROBLEM !!!!!!!!
215    
216    
217    
218     /**
219     * This var holds the locator metadata hash
220     * that is used to feed metadata to a per-query-instance
221     * of an Adapter object.
222     *
223     */
224     //var $_locator = NULL;
225    
226     /**
227     * This var holds the locator metadata hash
228     * which is built from some predefined rules
229     * using metadata from $_options and some
230     * other presets.
231     *
232     * See '_buildLocator' which acts as a dispatcher
233     * depending on $_options[datasource].
234     * (main dispatching level)
235     *
236     * The structure of a full blown locator looks like this:
237     *
238     * $locator = array(
239     * type => '<your type specifying the datasource-type>',
240     * metadata => array(
241     * ... your arbitrary deep metadata structure ...
242     * ),
243     * [dsn => '<your dsn markup>'],
244     * );
245     *
246     * Example 1 - data is inside a rdbms, using a dsn to connect to it:
247     * $locator = array(
248     * type => 'sql',
249     * dsn => 'mysql://username:password@localhost/database',
250     * );
251     *
252     * Example 2 - data is inside an odbms, reachable by doing remote procedure calls (rpc):
253     * $locator = array(
254     * type => 'rpc',
255     * metadata => array(
256     * package => 'Data::Driver::Proxy',
257     * Host => 'localhost',
258     * Port => '8765',
259     * )
260     * );
261     *
262     */
263     var $_locator = NULL;
264    
265    
266     /**
267     * This var holds the module information required to
268     * create instances of a Proxy and an Adapter.
269     * Information might get extracted from a
270     * DataSource::Locator instance.
271     *
272     * --- A skeleton:
273     *
274     * $this->_modules = array(
275     * _proxy => array( _id => '', _module => '', _options = array(), _instance => &$obj),
276     * _adapter => array( _id => '', _module => '', _options = array(), _instance => &$obj),
277     * );
278     *
279     *
280     * --- An example:
281     *
282     * $this->_modules = array(
283     * _proxy => array( _id => 'rpc', _module => 'DesignPattern::RemoteProxy', _instance => &$obj),
284     * _adapter => array( _id => 'phpHtmlLib', _module => 'DataSource::Adapter::phpHtmlLib::DataListSource', _instance => &$obj),
285     * );
286     *
287     *
288     */
289     //var $_modules = NULL;
290    
291    
292     /**
293     * This var holds the query hash
294     * that is used to feed as query to a per-query-instance
295     * of a Data::Driver::Proxy object.
296     *
297     */
298     var $_query = NULL;
299    
300    
301     /**
302     * this holds the query result from the
303     * Data::Driver::Proxy->queryXyz() call
304     *
305     */
306     var $_result = NULL;
307    
308    
309    
310     /**
311     * The constructor is used to pass in the
312     * locator metadata hash.
313     *
314     * @param LocatorMetadataHash array - layout: array( type => '', metadata => '', dsn => '' )
315     * @param Query array - layout: array( type => '', metadata => '', dsn => '' )
316     */
317    
318    
319     function DataSource_Generic( &$locator, $query ) {
320    
321     // copy parameter from query to locator
322     //$this->_locator->merge_to($this->_locator, array( datasource_type => $query[datasource] ), '_');
323     //$this->_locator->_datasource_type = $query[datasource];
324     //$locator[_datasource_type] = $query[datasource];
325    
326     // build master locator
327     $this->_locator = php::mkComponent('DataSource::Locator', $locator, array( datasource_type => $query[datasource] ) );
328     //exit;
329     //$this->_locator = php::mkComponent('DataSource::Locator');
330    
331     $this->_locator->check();
332    
333     // trace
334     //print "locator-obj: " . Dumper($this->_locator);
335     //print "locator-data: " . Dumper($locator_data);
336     //exit;
337    
338    
339    
340     /*
341     $this->_modules = array(
342     _proxy => array( _id => $locator->_datasource_type ),
343     _adapter => array( _id => $locator->_adapter_type ),
344     );
345     */
346    
347     //$this->set_locator( $locator );
348     $this->set_query( $query );
349    
350    
351    
352     /*
353     // V1:
354     // pre-flight: establish and check locator metadata
355     $this->_buildLocator();
356     $this->_checkLocator();
357     */
358    
359    
360    
361     // pre-flight: check locator metadata
362     //$locator->build();
363     if (!$this->_locator->check()) {
364     user_error("locator-check failed.");
365     exit;
366     //return;
367     }
368    
369     //exit;
370    
371    
372     // --- Proxy selector/dispatcher ...
373    
374     switch ($this->_locator->_datasource_type) {
375     case 'rpc':
376     //$locator_data = $this->_locator->get_metadata();
377     //$this->_modules[_proxy][_options] = array( locator => $this->_locator, query => $query );
378     //$this->_modules[_proxy][_module] = 'DataSource::Proxy::XMLRPC';
379    
380     //$this->_modules[_proxy][_module] = 'DesignPattern::RemoteProxy';
381     //$this->_modules[_proxy][_options] = $locator_data;
382    
383     //$this->set_component_name('DesignPattern::RemoteProxy');
384     //$this->set_component_options($locator_data);
385    
386     $this->set_component_name('DesignPattern::RemoteProxy');
387    
388     break;
389     default:
390     user_error("Site::GenericPage: no Proxy could be selected!");
391     break;
392     }
393    
394    
395    
396     /*
397     // V1:
398     // make up dummy args by now
399     $args = array(
400     adapter => 'phpHtmlLib',
401     );
402     */
403    
404     // V2: now propagated from caller!
405    
406     //print Dumper(array( locator => $locator, query => $query ));
407     //exit;
408    
409     //$this->create_adapter($this->module, $this->function, $this->arguments);
410     //$this->set_handler( $this->get_adapter() );
411    
412    
413     // --- Adapter selector/dispatcher ...
414     // ... resolves this._modules.adapter._id to this._modules.adapter._module
415    
416     switch ($locator->_adapter_type) {
417     case 'phpHtmlLib':
418     //$adapter_arguments = $args[title];
419    
420     $this->set_adapter_module('DataSource::Adapter::phpHtmlLib::DataListSource');
421    
422     // in order to let the Adapter communicate with the Proxy,
423     // instantiate a wrapper method in a third namespace via
424     // Exporter::export_symbol(from, to)
425     $this->set_adapter_options($this);
426    
427    
428     break;
429     default:
430     user_error("DataSource::GenericDataSource: no Adapter could be selected!");
431     break;
432     }
433    
434    
435    
436     // --- module instantiation - Proxy and Adapter
437    
438     //user_error("handle proxy here!");
439     //$this->create_proxy();
440    
441     //print Dumper($this);
442    
443     //print "this: " . Dumper($this);
444    
445     // V1:
446     /*
447     $proxy = php::mkComponent($this->_modules[_proxy][_module], $this->_modules[_proxy][_options]);
448     print "proxy: " . Dumper($proxy);
449     //exit;
450     */
451    
452     // V2:
453     /*
454     $resultHandle = mkObject('DesignPattern::RemoteProxy', $cache_key, array( key => 1, command => $command, query => $query, remote => 1, rpcinfo => $rpcinfo, cache => array( db => 0, session => 1 ) ) );
455     $result = $resultHandle->getAttributes();
456     return $result;
457     */
458    
459     // V3:
460     // $this->set_handler( $proxy );
461    
462     // V4: use _component_name & _component_options (proposal from GenericPage)
463     // $this->_component_name = ...
464     // $this->create_handler();
465    
466    
467     // V1:
468     //$this->create_adapter($adapter_module, $this->function, $this->arguments);
469    
470     // V2: FIXME - better use V1?
471     $adapter = php::mkComponent($this->get_adapter_module(), $this->get_adapter_options());
472     $this->set_adapter($adapter);
473    
474    
475    
476    
477     }
478    
479     function make_adapter_transparent() {
480    
481     // ... known from Site::Adapter::php::Class:
482    
483     // At this place the Adapter becomes a Proxy.
484     // This functionality currently correlates to the
485     // name of our ancestor, DesignPattern::AdapterProxy.
486     // FIXME: move this code to its namespace!!!
487     // FIXME: rename to set_proxy...
488    
489     // The reason this has to occour here is required
490     // by the TransparentProxy to actually make the
491     // _handler (_proxy) transparent later....
492     //$this->set_proxy( $this->get_adapter() );
493     }
494    
495     /**
496     * Set the metadata information
497     * (a DataSource::Locator) we will use
498     * to build encapsulated instances
499     * of both a Proxy and an Adapter.
500     *
501     * @param LocatorMetadataHash array -
502     *
503     */
504     function set_metadata( &$locator ) {
505     $this->_locator = &$locator;
506     }
507    
508     function &get_metadata() {
509     return $this->_locator;
510     }
511    
512    
513     /**
514     * Set the query metadata hash we will feed *completely*
515     * to an encapsulated Proxy instance.
516     *
517     * @param QueryDeclaration array -
518     *
519     */
520     function set_query( $query ) {
521     $this->_query = $query;
522     }
523    
524     /**
525     * Issue remote/proxy call
526     * Stolen from Application_AbstractBackend::_remote_method (RefactoringProposal?)
527     * Tweaked a bit: proxy package now taken from $this->_handler_name
528     *
529     * Create a new instance of a proxy and store it for later reuse.
530     * Read the locator metadata hash and create
531     * the proxy handler instance using passed-in name.
532     *
533     * @param string - $proxy_name (namespaced classname - perl syntax - e.g.: Data::Driver::Proxy)
534     *
535     */
536     function datasource_handler_call() {
537    
538    
539     // 1. read args and build cache key
540    
541     $arg_list = func_get_args();
542     $command = array_shift($arg_list);
543     $cache_key = join("-", array(session_id(), $command, join('_', $arg_list) ));
544     //print "cache_key: $cache_key<br>";
545    
546     // FIXME: what if arg_list still contains more elements after doing this?
547     $query = array_shift($arg_list);
548    
549     // 2. prepare proxy-options (read from locator)
550    
551     //print "this: " . Dumper($this);
552     //exit;
553    
554     // read from locator metadata
555     //$proxy_name = $this->_locator[metadata][package];
556    
557     // V1:
558     //$rpcinfo = array( Host => $this->_locator[metadata][Host], Port => $this->_locator[metadata][Port] );
559    
560     // V2:
561     $rpcinfo = $this->_locator->get_metadata();
562    
563     // FIXME! implement this into Data::Driver::RPC::Remote!
564     //$rpcinfo[to_latin] = 1;
565    
566     // FIXME! patch query
567     if (sizeof($query) == 1) {
568     $query = $query[0];
569     }
570    
571     //print "query: " . Dumper($query);
572    
573     // !!! use DesignPattern::Proxy here !!!
574     // $proxy = new DesignPattern_Proxy($proxy_name, ...)
575     // or:
576     // $proxy = mkObject('DesignPattern::Proxy');
577     // $this->set_handler( $proxy );
578     // or:
579     // $proxy = mkObject('DesignPattern::Proxy');
580     // $proxy->
581     // $this->set_handler( $proxy );
582    
583    
584    
585     //$this->set_component_name( $proxy_name );
586     $this->set_component_options( $cache_key, array( key => 1, command => $command, query => $query, remote => 1, rpcinfo => $rpcinfo, cache => array( db => 0, session => 1 ) ) );
587     //print Dumper($this);
588     //exit;
589    
590     $this->create_proxy();
591    
592     //print Dumper($this);
593     //exit;
594    
595     /*
596     // -------------------- clone this & modify ----------
597     $proxy = mkObject($proxy_name, $cache_key, array( key => 1, command => $command, query => $query, remote => 1, rpcinfo => $rpcinfo, cache => array( db => 0, session => 1 ) ) );
598     $this->set_handler( $proxy );
599     //$result = $resultHandle->getAttributes();
600     //return $result;
601     //$this->_result = $resultHandle->getAttributes();
602     //$this->_result = $this->_handler->getAttributes();
603     // -------------------- clone this & modify ----------
604     */
605    
606     }
607    
608    
609     function datasource_handler_buildoptions() {
610    
611     //print Dumper($this->_query);
612     //exit;
613    
614     // make up a command from metadata
615     // FIXME: abstract this some more (e.g. via a CommandMapper|Registry)
616     switch ($this->_query[metatype]) {
617     case 'data':
618     //$command = 'queryData';
619     $command = 'getObjects'; // FIXME!!!
620     //$this->_locator->set_option('metadata.command', $command);
621     $args = array();
622     switch ($this->_query[vartype]) {
623     case 'object':
624     if (!$this->_query[classname]) {
625     $msg = "_query[vartype] == 'object' requires _query[classname]";
626     user_error("GenericDataSource::query_data() - failed: " . $msg);
627     }
628     array_push($args, $this->_query[classname]);
629     break;
630     }
631     break;
632     case 'schema':
633     $command = 'querySchema';
634     break;
635     }
636    
637    
638     /*
639     $this->_query[rpc_command] = $command;
640     $this->_query[rpc_args] = $command;
641     */
642    
643     // methods!!!
644     $this->_locator->_call[_method] = $command;
645     $this->_locator->_call[_arguments] = $args;
646    
647     //$adapter = $this->get_adapter();
648    
649     // trace
650     /*
651     print Dumper(null, '<b>= = = = = = = = = = = = = = = = = = = = = =</b>');
652     print Dumper('datasource_handler_buildoptions', array(
653     "_locator" => $this->_locator,
654     "_query" => $this->_query,
655     "command" => '<b>' . $command . "</b>",
656     "args" => $args
657     ));
658     */
659    
660     /*
661     $this->_handler_options = array(
662     method => $command,
663     args => $args,
664     );
665     */
666    
667     }
668    
669     function &fetch_result() {
670    
671     $this->datasource_handler_buildoptions();
672    
673     $method = $this->_locator->_call[_method];
674     $args = $this->_locator->_call[_arguments];
675    
676     // pre-flight checks
677     if (!$method) {
678     $msg = "Remote command could not be resolved, please pass in or check configuration.";
679     user_error("GenericDataSource::query_data() - failed: " . $msg);
680     return;
681     }
682    
683     //print "fetch_result: this->_handler=" . Dumper($this->_handler);
684    
685     // do remote call here and get result
686     // FIXME: handle synchronous/asynchronous mode here!!!
687     $this->datasource_handler_call($method, $args);
688    
689    
690     // TODO: ... = $this->poll_handler_result and $this->get_handler_result
691     $proxy = $this->get_proxy();
692     $this->_result = $proxy->getResult();
693     //print "result: " . Dumper($this->_result); exit;
694    
695     $this->_result_count = sizeof($this->_result);
696    
697     // trace
698     if ($this->_debug[notice]) {
699     //print "_result = " . Dumper($this->_result);
700     print "DataSource::Generic->_result_count = " . $this->_result_count . "<br/>";
701     }
702    
703     return $this->_result;
704    
705     }
706    
707    
708     function &query_data() {
709     //print "query!<br/>";
710     return $this->fetch_result();
711     //$this->handle_result();
712     }
713    
714     function query_schema() {
715     user_error("FIXME: query_schema");
716     // $this->datasource_handler_call( ... );
717     }
718    
719     function &get_result() {
720     return $this->_result;
721     }
722    
723     }
724    
725     ?>

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