/[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.5 - (hide annotations)
Tue Feb 11 05:26:04 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
Changes since 1.4: +113 -7 lines
+ sub _tellWhatIWillDo
+ re-enabled "branch to execution path for special targets" mechanism

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

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