/[cvs]/nfo/perl/libs/Data/Transfer/Sync/API.pm
ViewVC logotype

Annotation of /nfo/perl/libs/Data/Transfer/Sync/API.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.7 - (hide annotations)
Fri Feb 21 08:00:24 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.6: +18 -4 lines
debugging

1 joko 1.7 ## $Id: API.pm,v 1.6 2003/02/14 12:58:47 joko Exp $
2 joko 1.1 ##
3     ## Copyright (c) 2002 Andreas Motl <andreas.motl@ilo.de>
4     ##
5     ## See COPYRIGHT section in pod text below for usage and distribution rights.
6     ##
7     ## ----------------------------------------------------------------------------------------
8 joko 1.2 ## $Log: API.pm,v $
9 joko 1.7 ## Revision 1.6 2003/02/14 12:58:47 joko
10     ## + re-enabled the erase-mechanism
11     ##
12 joko 1.6 ## Revision 1.5 2003/02/11 05:26:04 joko
13     ## + sub _tellWhatIWillDo
14     ## + re-enabled "branch to execution path for special targets" mechanism
15     ##
16 joko 1.5 ## Revision 1.4 2003/02/09 05:03:02 joko
17     ## + minor fix regarding namespace of api versioning extension module
18     ##
19 joko 1.4 ## Revision 1.3 2003/02/09 04:59:27 joko
20     ## + api versioning mechanism
21     ## + major structure changes
22     ## - refactored code to sister modules
23     ##
24 joko 1.3 ## Revision 1.2 2003/01/20 16:59:48 joko
25     ## + cosmetics and debugging
26     ##
27 joko 1.2 ## Revision 1.1 2003/01/19 01:23:04 joko
28     ## + new from Data/Transfer/Sync.pm
29     ##
30 joko 1.1 ## ----------------------------------------------------------------------------------------
31    
32    
33 joko 1.2 package Data::Transfer::Sync::API;
34    
35 joko 1.1 use strict;
36     use warnings;
37 joko 1.2
38 joko 1.3 use base qw( DesignPattern::Bridge );
39    
40 joko 1.1 use mixin::with qw( Data::Transfer::Sync );
41 joko 1.2
42 joko 1.1
43     # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - main
44 joko 1.2
45     use Data::Dumper;
46 joko 1.3 use Hash::Merge qw( merge );
47 joko 1.2
48 joko 1.3 use Data::Compare::Struct qw( isEmpty );
49 joko 1.2
50 joko 1.1 # get logger instance
51     my $logger = Log::Dispatch::Config->instance;
52 joko 1.2
53 joko 1.3
54     sub api_constructor {
55 joko 1.1 my $self = shift;
56 joko 1.3 $logger->debug( __PACKAGE__ . "->api_constructor: Loading API");
57     $self->_loadVersionExtensions();
58 joko 1.1 }
59 joko 1.2
60 joko 1.3
61     sub _loadVersionExtensions {
62 joko 1.1 my $self = shift;
63 joko 1.3 my $syncVersion = $self->{sync_version};
64     $syncVersion ||= '';
65     $logger->debug( __PACKAGE__ . "->loadVersionExtensions( version='$syncVersion' )");
66     #print Dumper($self);
67     #exit;
68 joko 1.4 my $module = "API::$syncVersion";
69 joko 1.3 $self->load($module);
70     }
71    
72    
73     sub configure {
74     my $self = shift;
75    
76     $logger->debug( __PACKAGE__ . "->configure");
77    
78     my @args = @_;
79    
80     if (!isEmpty(\@args)) {
81     my %properties = @_;
82     # merge args to properties
83     #map { $self->{$_} = $properties{$_}; } keys %properties;
84     #print Dumper($self);
85     #print Dumper(\%properties);
86     if ($self->{options}) {
87     my $options_new = merge($self->{options}, \%properties);
88     #print Dumper($options_new);
89     $self->{options} = $options_new;
90     #print Dumper($self->{options});
91     } else {
92     $self->{options} = \%properties;
93     }
94     $self->_init();
95     #$self->_initV1();
96     } else {
97     #print "no args!", "\n";
98 joko 1.1 }
99    
100 joko 1.3 #print Dumper($self);
101     #exit;
102    
103     $self->{state}->{configured} = 1;
104     return 1;
105 joko 1.1 }
106 joko 1.2
107 joko 1.3
108     sub setArguments {
109 joko 1.1 my $self = shift;
110 joko 1.3 my $args_raw = shift;
111     $self->{args_raw} = $args_raw;
112     }
113 joko 1.1
114 joko 1.3 sub readArguments {
115     my $self = shift;
116 joko 1.1
117 joko 1.3 my %syncConfig;
118     tie %syncConfig, 'Tie::IxHash';
119     %syncConfig = (
120     map => {
121     moduleName => $self->{args_raw}->{'mapping-module'},
122     },
123     source => {
124     dbKey => $self->{args_raw}->{source},
125     nodeType => $self->{args_raw}->{'source-type'},
126     nodeName => $self->{args_raw}->{'source-node'},
127     },
128     target => {
129     dbKey => $self->{args_raw}->{target},
130     nodeName => $self->{args_raw}->{'target-node'},
131     },
132     process => {
133     mode => $self->{args_raw}->{mode},
134     erase => $self->{args_raw}->{erase},
135     import => $self->{args_raw}->{import},
136     prepare => $self->{args_raw}->{prepare},
137     },
138     # metadata => {
139     # config => $self->{config_metadata},
140     # }
141     );
142 joko 1.1
143 joko 1.3 $self->{args} = \%syncConfig;
144 joko 1.1
145     }
146    
147 joko 1.2
148 joko 1.3 # TODO: some feature to show off the progress of synchronization (cur/max * 100)
149     sub syncNodes {
150 joko 1.1
151     my $self = shift;
152 joko 1.3 my $args = shift;
153    
154 joko 1.7 #$logger->notice( "========================== " . __PACKAGE__ . "->syncNodes ==============");
155 joko 1.3 $logger->debug( __PACKAGE__ . "->syncNodes: starting" );
156 joko 1.1
157 joko 1.3 #print Dumper($self);
158 joko 1.1 #exit;
159    
160 joko 1.3 #print Dumper($self->{options});
161     $self->_prepareOptions();
162 joko 1.1
163 joko 1.6 # trace
164     #print Dumper($self->{options});
165     #exit;
166 joko 1.1
167 joko 1.3 if (!$self->checkOptions()) {
168     $logger->critical( __PACKAGE__ . "->syncNodes: 'Data::Transfer::Sync::checkOptions' failed.");
169 joko 1.1 return;
170     }
171    
172 joko 1.3 if (!$self->{state}->{configured}) {
173     $logger->critical( __PACKAGE__ . "->syncNodes: Synchronization object is not configured/initialized correctly." );
174 joko 1.1 return;
175     }
176    
177 joko 1.5 #print Dumper($args);
178    
179 joko 1.3 # remember arguments through the whole processing
180     $self->{args} = $args;
181 joko 1.1
182 joko 1.3 # hash to hold and/or fill in metadata required for the processing
183     $self->{meta} = {};
184 joko 1.1
185 joko 1.3 # hash to sum up results
186     # TODO: re-implement! (sync-statistics???)
187 joko 1.1
188 joko 1.6 #print Dumper($self->{options}->{process});
189    
190 joko 1.3 # detect synchronization method to determine which optical symbol (directed arrow) to use
191     my $mode = $self->{args}->{mode}; # V1
192     $mode ||= $self->{options}->{process}->{mode}; # V2
193     my $direction_arrow = $self->_getDirectedArrow($mode);
194 joko 1.1
195 joko 1.3 # determine code versions
196     my $code_metadata_version;
197     # first, try to use version supplied by mapping-metadata
198     $code_metadata_version = $self->{options}->{metadata}->{version};
199     # if not set, inherit from global 'sync_version'
200     $code_metadata_version ||= $self->{sync_version};
201    
202     # load additional code from versioned namespace into current instance
203     my $dynModule = "Metadata::$code_metadata_version";
204     $self->load($dynModule);
205    
206     # build metadata using versioned code and stuff
207     $self->options2metadata();
208     $self->options2metadata_accessor();
209    
210 joko 1.5 # branch to execution path for special targets
211     # detect for option 'handler' which could be a CODEref
212     if ($self->{options}->{handler} && ref $self->{options}->{handler} eq 'CODE') {
213     $logger->info( __PACKAGE__ . "->syncNodes: Running (special handler code - no generic sync!) on '$self->{options}->{target}->{dbKey}' with MODE $self->{options}->{process}->{mode}, NODE $self->{options}->{target}->{address}");
214     #print Dumper($self);
215     #exit;
216     # don't do this any more - it wasn't very nice somehow ...
217     #$self->{options}->{handler}->($self->{app}, $self->{options});
218     # .... now: better let the parent application scope handle this via callback
219     # not any more required for this: $self->{app} inside here (which isn't the app we mean here)
220     # required for this: getting the options out of here: establish some getter method! ($self->getOptions(...))
221     # so....
222     #$self->{__bridge}->{parent_ref}->
223    
224     # ahh okay, DesignPattern::Bridge moves closer to some Class::Inner!???
225     # so...
226     # similar like above - but it isn't very nice anyway ... (no privateness, but: who cares?)
227     #print Dumper($self->{__bridge});
228     # just take the global application instance and
229     # throw it into the context of the mapping module - this is heavy! ;-) (but again, who cares...)
230 joko 1.6 # TODO: handle this more abstract *sometime*
231 joko 1.5 #$self->{options}->{handler}->($self->{__bridge}->{parent}->{app}, $self->{options});
232     $self->{options}->{handler}->($self->{__bridge}->{parent}->{process}, $self->{options});
233    
234     return;
235     }
236    
237     # TODO: execution path branch V2!!!
238     # option1: wrap this via callback to parent scope (like current impl. mechanism)
239     # option2: branch directly from here (this needs refactoring of the sub handler)
240    
241 joko 1.3 # tracing
242     #print Dumper($self);
243     #exit;
244 joko 1.1
245 joko 1.7 # V1:
246     $logger->debug( __PACKAGE__ . "->syncNodes: source=$self->{meta}->{source}->{dbKey}/$self->{meta}->{source}->{nodeName} [$self->{meta}->{source}->{nodeType}] $direction_arrow target=$self->{meta}->{target}->{dbKey}/$self->{meta}->{target}->{nodeName} [$self->{meta}->{target}->{nodeType}]" );
247    
248     # V2:
249     my $what = "$self->{meta}->{source}->{dbKey}/$self->{meta}->{source}->{nodeName} [$self->{meta}->{source}->{nodeType}] $direction_arrow $self->{meta}->{target}->{dbKey}/$self->{meta}->{target}->{nodeName} [$self->{meta}->{target}->{nodeType}]";
250     #my $header = ("~.." x 7) . " " . $what . " " . ("~.." x 4);
251     #my $header = ("= " x 7) . " " . $what . " " . ("= " x 4);
252     #my $header = ("~=-_-=" x 3) . " " . $what . " " . ("~=-_-=" x 4);
253     my $header = ("_-=~=-" x 4) . " " . $what . " " . ("_-=~=-" x 4);
254     $logger->notice($header);
255    
256 joko 1.1
257 joko 1.3 return if !$self->buildFieldmapping();
258     return if !$self->_touchNodeSet();
259     return if !$self->_prepare_sync();
260 joko 1.5 $self->_tellWhatIWillDo();
261 joko 1.3
262     # tracing
263     #print Dumper($self);
264     #print Dumper($self->{args});
265     #print Dumper($self->{options});
266     #print Dumper($self->{meta});
267     #print Dumper($self->{metadata});
268     #exit;
269 joko 1.1
270 joko 1.3 # finally, tell the core to start the synchronization process
271     $self->_run();
272    
273     }
274 joko 1.1
275 joko 1.2
276 joko 1.5 my $c_string_default = '';
277     sub c_string {
278     my $value = shift;
279     $value ||= "[$c_string_default]";
280     return $value;
281     }
282    
283     sub _tellWhatIWillDo {
284     my $self = shift;
285    
286    
287     #return;
288    
289     # trace
290     #print Dumper($self->{meta});
291     #exit;
292    
293     $c_string_default = 'n/a';
294     my $source = c_string($self->{opt}->{'source'});
295     my $source_node = c_string($self->{opt}->{'source-node'});
296     my $source_type = c_string($self->{opt}->{'source-type'});
297     my $target = c_string($self->{opt}->{'target'});
298     my $target_node = c_string($self->{opt}->{'target-node'});
299     my $target_type = c_string($self->{opt}->{'target-type'});
300    
301     my $mapping_module = c_string($self->{opt}->{'mapping-module'});
302     my $mode = uc c_string($self->{opt}->{'mode'});
303    
304     #my $ql = "$mode INTO $source NODE $source_node TYPE $source_type SELECT NODE $target_node TYPE $target_type FROM $target USING MODULE $mapping_module;";
305     #$logger->notice( __PACKAGE__ . ": $ql" );
306     my $ql = <<EOT;
307    
308     FETCH DATA
309     FROM STORAGE $self->{meta}->{source}->{dbKey}
310     AT NODE $self->{meta}->{source}->{accessorName}.$self->{meta}->{source}->{nodeName}
311     USING IDENTIFIER $self->{meta}->{source}->{IdentProvider}->{method}.$self->{meta}->{source}->{IdentProvider}->{arg}
312     CONVERT DATA
313     CAST FROM $self->{meta}->{source}->{nodeType} TO $self->{meta}->{target}->{nodeType}
314     MAP ATTRIBUTES FROM @{$self->{meta}->{source}->{childnodes}} TO @{$self->{meta}->{target}->{childnodes}}
315     STORE DATA
316     TO STORAGE $self->{meta}->{target}->{dbKey}
317     AT NODE $self->{meta}->{target}->{accessorName}.$self->{meta}->{target}->{nodeName}
318     USING IDENTIFIER $self->{meta}->{target}->{IdentProvider}->{method}.$self->{meta}->{target}->{IdentProvider}->{arg}
319     EOT
320    
321    
322 joko 1.7 chomp($ql);
323     $logger->info($ql);
324 joko 1.5
325     #exit;
326     return;
327    
328     my $actioning = ucfirst $self->{opt}->{'action'} . 'ing';
329    
330     # FIXME: this is weird!
331     my $long = <<EOT;
332    
333     - $actioning data of type $target_type and
334     filtered by $target_node from the storage named $target
335     to the storage named $source - filtered by $source_node.
336     - Will attempt to convert the data to $source_type.
337     EOT
338     chomp($long);
339     $logger->notice( __PACKAGE__ . ": $long" );
340    
341     }
342    
343    
344 joko 1.3 sub _prepareOptions {
345 joko 1.1
346     my $self = shift;
347 joko 1.3
348     my $opts = $self->{args};
349 joko 1.1
350     # patch options
351 joko 1.3 $opts->{source}->{nodeName} ||= '';
352     $opts->{target}->{nodeName} ||= '';
353 joko 1.1 $opts->{process}->{mode} ||= '';
354 joko 1.3 $opts->{process}->{erase} ||= 0;
355 joko 1.1 $opts->{process}->{prepare} ||= 0;
356 joko 1.3
357     # defaults (mostly for backward-compatibility to V1 -
358     # but code mungled here out of prepareOptions_V1 from Version::V1)
359     $opts->{metadata}->{syncMethod} ||= 'checksum'; # | timestamp
360     $opts->{source}->{ident} ||= 'storage_method:id';
361     $opts->{source}->{exclude} ||= [qw( cs )];
362     $opts->{target}->{ident} ||= 'property:oid';
363     #$map->{source_node} ||= $source_node_name;
364     #$map->{direction} ||= $opts->{mode}; # | PUSH | PULL | FULL
365 joko 1.1
366     # pre-check options
367     if (!$self->_preCheckOptions($opts)) {
368 joko 1.3 $logger->error( __PACKAGE__ . "->_prepareOptions: _preCheckOptions failed.");
369 joko 1.1 return;
370     }
371    
372     # inform user about option preparation
373 joko 1.3 $logger->debug( __PACKAGE__ . "->_prepareOptions( source.node='$opts->{source}->{nodeName}', target.node='$opts->{target}->{nodeName}', mode='$opts->{process}->{mode}', e='$opts->{process}->{erase}', p='$opts->{process}->{prepare}' )");
374 joko 1.1
375     # try to load mapping-metadata-container
376 joko 1.3 # How? Create a new instance of the given
377     # perl module/package name in ->{...}->{moduleName}.
378     # This instance is used later in the innards of the sync,
379     # that's why the module (class) should have a certain layout
380     # enabling the core to use it for gathering metadata while processing.
381     my $mapObject = DesignPattern::Object->fromPackage($opts->{map}->{moduleName});
382 joko 1.1
383     # try to resolve map from metadata-container
384    
385     # type of the item/node
386     my $source_nodeType = $opts->{source}->{nodeType};
387    
388     # check if mapping for certain node is contained in mapping object
389 joko 1.3 if (!$mapObject || !$mapObject->can($source_nodeType)) {
390     $logger->warning( __PACKAGE__ . "->_prepareOptions: Can't access mapping for source-type=\"$source_nodeType\" - please check \"$opts->{map}->{moduleName}\".");
391 joko 1.1 return;
392     }
393    
394     # get map
395     my $map = $mapObject->$source_nodeType;
396     #print Dumper($map);
397    
398 joko 1.3 my $map_version = $map->{metadata}->{version};
399     # FIXME: backward compatibility
400     if (!$map_version) {
401     $self->options_to_V2($map);
402     }
403    
404     # trace
405     #print Dumper($map);
406     #exit;
407     #print "ref: ", ref $map->{target}, "\n";
408     #print "target: ", $map->{target}, "\n";
409    
410 joko 1.1 # check here if "target" is actually a CODEref - in this case: resolve it - deprecated!!! ???
411     if (ref $map->{target}->{address} eq 'CODE') {
412     $map->{target}->{address} = $map->{target}->{address}->($source_nodeType);
413     }
414    
415     # resolve expressions (on nodename-level) here
416 joko 1.3 elsif ($map->{target}->{address} =~ m/^(code|expr):(.+?)$/) {
417 joko 1.1 my $target_dynamic_type = $1;
418     my $target_dynamic_expression = $2;
419     if (lc $target_dynamic_type eq 'code') {
420     $map->{target} = $mapObject->$target_dynamic_expression($map);
421     }
422     }
423    
424 joko 1.3 # merging - V1
425     #map { $opts->{$_} = $map->{$_}; } keys %$map;
426     # trace
427     #print Dumper($self->{options});
428    
429     # merging - V2
430    
431     # merge local map with local opts
432 joko 1.5
433     # delete undef'd items in $map
434    
435 joko 1.3 # enable cloning
436     # FIXME: do we really need cloning here? trade safety/encapsulation for speed?
437     Hash::Merge::set_clone_behavior(1);
438     Hash::Merge::set_behavior( 'RIGHT_PRECEDENT' );
439 joko 1.5 #Hash::Merge::set_behavior( 'STORAGE_PRECEDENT' );
440     #Hash::Merge::set_behavior( 'RETAINMENT_PRECEDENT' );
441     # TODO: add an option to Hash::Merge not to overwrite set items with undefined/empty/not assigned ones
442 joko 1.3 my $locals_merged = merge( $opts, $map );
443    
444     # trace
445     #print Dumper($opts);
446     #print Dumper($map);
447     #print Dumper($locals_merged);
448     #exit;
449    
450     # merge local-merged ones with instance-wide options
451     Hash::Merge::set_clone_behavior(0);
452     $self->{options} = merge( $self->{options}, $locals_merged );
453    
454     # trace
455     #print Dumper($self->{options});
456     #exit;
457    
458 joko 1.1
459     $self->{state}->{options_ready} = 1;
460    
461     return 1;
462    
463     }
464 joko 1.2
465    
466 joko 1.3 sub _preCheckOptions {
467    
468 joko 1.1 my $self = shift;
469 joko 1.3 my $opts = shift;
470    
471     # trace
472     #print Dumper($opts);
473     #exit;
474    
475     if (!$opts->{process}->{mode}) {
476     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"--action=(load|save)\".");
477     return;
478 joko 1.1 }
479    
480 joko 1.3 # the type of the to-be-synced item
481     if (!$opts->{source}->{nodeType}) {
482     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-type\".");
483     return;
484 joko 1.1 }
485 joko 1.3 # the name of the (container-) node the items are listed in
486     if (!$opts->{source}->{nodeName}) {
487     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-node\".");
488     return;
489 joko 1.2 }
490 joko 1.1
491 joko 1.3 # a "map"-declaration which module to use for mapping- and/or lookup-purposes
492     if (!$opts->{map}) {
493     $logger->warning( __PACKAGE__ . "->_preCheckOptions: No mapping supplied - please check key 'map|mappings' in global configuration or specify additional argument '--mapping-module'.");
494 joko 1.1 return;
495     }
496 joko 1.3 if (!$opts->{map}->{moduleName}) {
497     $logger->warning( __PACKAGE__ . "->_preCheckOptions: Currently only perl-modules can provide mappings: Please specify one with '--mapping-module=My::Mapping::Module'.");
498     return;
499 joko 1.1 }
500    
501 joko 1.3 return 1;
502 joko 1.2
503     }
504 joko 1.1
505    
506 joko 1.2
507 joko 1.3 sub _prepare_sync {
508 joko 1.2
509     my $self = shift;
510 joko 1.1
511     # TODO:
512     # + if action == PUSH: start processing
513     # -+ if action == PULL: swap metadata and start processing
514     # - if action == FULL: start processing, then swap metadata and (re-)start processing
515    
516 joko 1.3 #print "dir: ", $self->{args}->{direction}, "\n";
517    
518 joko 1.1 # manipulate metainfo according to direction of synchronization
519 joko 1.3 if (lc $self->{options}->{process}->{mode} eq 'push') {
520     # just do it ... (don't modify any metadata)
521    
522     } elsif (lc $self->{options}->{process}->{mode} eq 'pull') {
523     # swap source and target metadata
524     # TODO: introduce different mechanism to make more then two partners (descents) possible
525 joko 1.1 ($self->{meta}->{source}, $self->{meta}->{target}) =
526     ($self->{meta}->{target}, $self->{meta}->{source});
527 joko 1.5 #($self->{options}->{source}, $self->{options}->{target}) =
528     # ($self->{options}->{target}, $self->{options}->{source});
529 joko 1.3
530     } elsif (lc $self->{options}->{process}->{mode} eq 'full') {
531     # TODO:
532     # do a pull first and a push afterwards
533     # this requires us to be called somehow recursively - just one recursion level ;-)
534    
535 joko 1.1 } else {
536 joko 1.3 # TODO: are there any other synchronization modes besides PULL, PUSH, FULL?
537    
538 joko 1.1 }
539    
540     # import flag means: prepare the source node to be syncable
541     # this is useful if there are e.g. no "ident" or "checksum" columns yet inside a DBI like (row-based) storage
542 joko 1.6 if ($self->{options}->{process}->{prepare}) {
543 joko 1.1 $self->_prepareNode_MetaProperties('source');
544     $self->_prepareNode_DummyIdent('source');
545     #return;
546     #$self->_erase_all($opts->{source_node});
547     }
548    
549     # erase flag means: erase the target
550     #if ($opts->{erase}) {
551 joko 1.6 if ($self->{options}->{process}->{erase}) {
552 joko 1.1 # TODO: move this method to the scope of the synchronization core and wrap it around different handlers
553     #print "ERASE", "\n";
554     $self->_erase_all('target');
555     }
556 joko 1.2
557     return 1;
558 joko 1.1
559 joko 1.3 }
560    
561     sub _getDirectedArrow {
562     my $self = shift;
563     my $mode = shift;
564     $mode ||= '';
565    
566     if (lc $mode eq 'push') {
567     return '->';
568     } elsif (lc $mode eq 'pull') {
569     return '<-';
570     } elsif (lc $mode eq 'full') {
571     return '<->';
572     } else {
573     return '';
574     }
575 joko 1.1 }
576    
577     1;

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